Introduction
cone rod homeobox (CRX) mutations can cause dominant Leber congenital amaurosis (LCA), a genetic disorder that can lead to blindness. Normally LCA is caused by recessive genes, but on rarer cases, it can be cause by dominant genes, such as CRX. We are look at Bulk RNA-seq data from retinal organoids derived from induced pluripotent stem cells (iPSCs) which is itself take from a LCA patient with CRX-I138fs mutation and their healthy parent. This mutation occurs in the transactivation domain, and so the effect of the mutation ist that CRX is not transcribed. The data source is GSE152939. The author is trying to show that a deno-associated virus gene therapy can restore RNA expression of CRX. However, although this study is about gene therapy, the data that show rescued expression is in single cell RNA-seq data, which is not in the scope of this report, so it was not included. The data was collected from 6 retinal organoids, for 90 days, 125 days, 150 days and 200 day after the iPSCs start to differentiate; 3 of the organoids were from LCA patient, and 3 organoids were from their healthy parent. (Kruczek et al. 2021)
Note that the vast majority of code in this report are take from BCB420 lecture (Isserlin 2022c, 2022a, 2022d, 2022b) with minor modification to make it appropriate for my data.
Note 2: I cannot put bibtex inline citatation within the codechunk, so it is left at the end of the code chunk, and sometimes there is a figure, so the figure legend/caption may be directly followed by the inline citation
Assignment 1 processing
Lets us first do all the processing that was done in assignment 1
source("./assignment_functions.r")
supplementaryFiles <- downloadSupplementaryFiles("GSE152939")
CRX_ExperimentRawCount <- readFile(supplementaryFiles)
CRX_ExperimentRawCountFiltered <- filterOutLowCount(CRX_ExperimentRawCount)
normalized_counts <- normalizeTheCounts(CRX_ExperimentRawCountFiltered)
(Isserlin 2022c, 2022a; Davis and Meltzer 2007; Durinck et al. 2009; Chen, Lun, and Smyth 2016)
ensemblDataSet <- getEnsemblbiomart()
(Isserlin 2022c, 2022a; Durinck et al. 2009)
normalized_counts_annot <- mapTheData(CRX_ExperimentRawCountFiltered, normalized_counts, ensemblDataSet)
(Isserlin 2022c, 2022a; Durinck et al. 2009)
saveRDS(normalized_counts_annot, file = "normalized_counts_annot.rds")
# Apprently there is a dupliate emseblem id
normalized_count_data <- normalized_counts_annot[!duplicated(normalized_counts_annot[ , c("ensembl_gene_id")]),]
normalized_counts_annot[duplicated(normalized_counts_annot[ , c("ensembl_gene_id")]),]
normalized_counts_annot[which(normalized_counts_annot$ensembl_gene_id == 'ENSG00000254876'), ]
Table 1a: The duplicate gene that was removed.
normalized_counts_annot[which(normalized_counts_annot$ensembl_gene_id == 'ENSG00000254876'), ]
Table 1b: showing the two gene has the same ensembl gene id, but not exactly the same hgnc symbol
I find the one that is consider as duplicate, then find the one that has the same enmselble gene id as it. For some reason this ensembl gene id maps to 2 hgnc symbols, SUGT1P4-STRA6LP and STRA6LP, even from the name it look like the same gene
normalized_count_cleaned <- normalized_count_data[normalized_count_data$ensembl_gene_id != 'ENSG00000156508', ]
Heatmap for all genes
first make a heat map matrix (the input thing use to generate a heat map)
heatmap_matrix <- normalized_count_cleaned[,
3:ncol(normalized_count_cleaned)]
rownames(heatmap_matrix) <- normalized_count_cleaned$ensembl_gene_id
colnames(heatmap_matrix) <- colnames(normalized_count_cleaned[, 3:ncol(normalized_count_cleaned)])
heatmap_matrix
saveRDS(heatmap_matrix, file = "heatmap_list.rds")
Table 2: The first 1000 row of the filtered normalized data for all samples, ordered by ensembl id
(Isserlin 2022d)
heatmap_matrix <- readRDS("./heatmap_list.rds")
then generate a heat map
library(ComplexHeatmap)
library(circlize)
if(min(heatmap_matrix) == 0){
heatmap_col = colorRamp2(c(0, max(heatmap_matrix)), c( "white", "red"))
} else {
heatmap_col = colorRamp2(c(min(heatmap_matrix), 0, max(heatmap_matrix)), c("blue", "white", "red"))
}
Heatmap(as.matrix(heatmap_matrix[1:1000,]),
show_row_dend = TRUE, show_column_dend = TRUE,
col=heatmap_col, show_column_names = TRUE,
show_row_names = FALSE, show_heatmap_legend = TRUE,
raster_resize_mat = TRUE)

Figure 1: Initial heatmap for differential expression with the first 1000 genes, order by ensembl id. Only the first 1000 genes are shown due to rendering resource issues (Isserlin 2022d; Gu, Eils, and Schlesner 2016; Gu et al. 2014)
Now with row normalization (i.e. the kind with subtract the mean divided by standard deviation) we can see each gene better
heatmap_matrix <- t(scale(t(heatmap_matrix)))
if(min(heatmap_matrix) == 0){
heatmap_col = colorRamp2(c(0, max(heatmap_matrix)), c( "white", "red"))
} else {
heatmap_col = colorRamp2(c(min(heatmap_matrix), 0, max(heatmap_matrix)), c("blue", "white", "red"))
}
Heatmap(as.matrix(heatmap_matrix[1:1000,]),
show_row_dend = TRUE,show_column_dend = TRUE,
col=heatmap_col,show_column_names = TRUE,
show_row_names = FALSE,show_heatmap_legend = TRUE)

Figure 2: Heatmap for differential expression of the first 1000 genes, ordered by ensembl id, after row normalization. Only the first 1000 genes are shown due to rendering resource issues (Isserlin 2022d; Gu, Eils, and Schlesner 2016; Gu et al. 2014)
you can see that the samples are more similar to each other at the same time point than whether or not it is control, I think that is reasonable since many gene get simulated and repressed during development, so the genes that are on is different from one development time point, to another.
Heatmap for tophits using Limma
We will be fitting the data to the a linear model and using empirical bayes to calculate p-values. Then we will using Benjamni-Hochberg for multiple hypothesis testing, for correcting our p-values.
Simple Model
The simple model is where the model accounts for the condition (control vs experiment), and the day that the data was collected on, but not account for which organoids is the data collected from.
samples <- data.frame(lapply(colnames(CRX_ExperimentRawCountFiltered)[3:ncol(CRX_ExperimentRawCountFiltered)], FUN=function(x){unlist(strsplit(x, split = "\\_"))[c(1, 2, 3)]}))
samples
colnames(samples) <- colnames(CRX_ExperimentRawCountFiltered)[3:ncol(CRX_ExperimentRawCountFiltered)]
rownames(samples) <- c("condition", "time","patient")
samples <- data.frame(t(samples))
model_design <- model.matrix(~ samples$condition+samples$time)
expressionMatrix <- as.matrix(normalized_count_data[,3:ncol(CRX_ExperimentRawCountFiltered)])
rownames(expressionMatrix) <-
normalized_count_data$ensembl_gene_id
colnames(expressionMatrix) <-
colnames(normalized_count_data)[3:ncol(CRX_ExperimentRawCountFiltered)]
minimalSet <- Biobase::ExpressionSet(assayData=expressionMatrix)
fit <- limma::lmFit(minimalSet, model_design)
fit2 <- limma::eBayes(fit,trend=TRUE)
topfit <- limma::topTable(fit2,
coef=ncol(model_design),
adjust.method = "BH",
number = nrow(expressionMatrix))
#merge hgnc names to topfit table
output_hits <- merge(normalized_count_data[,1:2],
topfit,
by.y=0,by.x=1,
all.y=TRUE)
#sort by pvalue
output_hits <- output_hits[order(output_hits$P.Value),]
Table 3: Showing for ecah sample, which organoid, at what time, and what condition it is
(Isserlin 2022d; Huber et al. 2015; Ritchie et al. 2015)
knitr::kable(output_hits[1:10,1:7],type="html",row.names = FALSE)
|
ensembl_gene_id
|
hgnc_symbol
|
logFC
|
AveExpr
|
t
|
P.Value
|
adj.P.Val
|
|
ENSG00000225840
|
|
-751.52636
|
278.308984
|
-34.89691
|
0
|
0
|
|
ENSG00000243199
|
|
-14.97061
|
6.890542
|
-30.68555
|
0
|
0
|
|
ENSG00000280614
|
|
-1251.17742
|
468.102238
|
-21.35881
|
0
|
0
|
|
ENSG00000280800
|
|
-1251.17742
|
468.102238
|
-21.35881
|
0
|
0
|
|
ENSG00000281181
|
|
-1251.17742
|
468.102238
|
-21.35881
|
0
|
0
|
|
ENSG00000276043
|
UHRF1
|
19.96635
|
8.419313
|
21.03885
|
0
|
0
|
|
ENSG00000109991
|
P2RX3
|
15.42168
|
4.861319
|
20.92939
|
0
|
0
|
|
ENSG00000151615
|
POU4F2
|
22.61264
|
6.640604
|
20.68397
|
0
|
0
|
|
ENSG00000147432
|
CHRNB3
|
8.65588
|
2.777748
|
20.59896
|
0
|
0
|
|
ENSG00000076003
|
MCM6
|
44.11041
|
47.070545
|
18.54065
|
0
|
0
|
Table 4: Show the limma output after fitting the data to the simple model, calculating the p-value, and correcting the p-value. This is only showing the first 10, ordered by p value. Note that the p value and adjusted p values are not actually 0, they are just rounded to 0
(Isserlin 2022d; Xie 2021)
This is how many genes that have pvalue under 0.05
length(which(output_hits$P.Value < 0.05))
[1] 8178
(Isserlin 2022d)
This is how many genes that still pvalue of less than 0.05 after adjustment
length(which(output_hits$adj.P.Val < 0.05))
[1] 6174
(Isserlin 2022d)
Organoid Model
The organoid model is where the model accounts for the condition (control vs experiment), and the day that the data was collected on, and for which organoids is the data collected from.
model_design_org <- model.matrix(
~ samples$patient + samples$condition + samples$time)
fit_pat <- limma::lmFit(minimalSet, model_design_org)
(Isserlin 2022d; Xie 2021)
Change the model, fit the data to the model, then apply empircal bayes , then correct with BH
fit_org <- limma::lmFit(minimalSet, model_design_org)
fit2_org <- limma::eBayes(fit_pat,trend=TRUE)
topfit_org <- limma::topTable(fit2_pat,
coef=ncol(model_design_org),
adjust.method = "BH",
number = nrow(expressionMatrix))
#merge hgnc names to topfit table
output_hits_org <- merge(normalized_count_data[,1:2],
topfit_org, by.y=0, by.x=1, all.y=TRUE)
#sort by pvalue
output_hits_org <- output_hits_org[order(output_hits_org$P.Value),]
(Isserlin 2022d; Ritchie et al. 2015)
knitr::kable(output_hits_org[1:10,1:7],type="html",row.names = FALSE)
|
ensembl_gene_id
|
hgnc_symbol
|
logFC
|
AveExpr
|
t
|
P.Value
|
adj.P.Val
|
|
ENSG00000225840
|
|
-751.52636
|
278.308984
|
-34.77848
|
0
|
0
|
|
ENSG00000243199
|
|
-14.97061
|
6.890542
|
-29.37584
|
0
|
0
|
|
ENSG00000151615
|
POU4F2
|
22.61264
|
6.640604
|
21.07068
|
0
|
0
|
|
ENSG00000147432
|
CHRNB3
|
8.65588
|
2.777748
|
21.01946
|
0
|
0
|
|
ENSG00000276043
|
UHRF1
|
19.96635
|
8.419313
|
20.97652
|
0
|
0
|
|
ENSG00000109991
|
P2RX3
|
15.42168
|
4.861319
|
20.54571
|
0
|
0
|
|
ENSG00000280614
|
|
-1251.17742
|
468.102238
|
-20.49586
|
0
|
0
|
|
ENSG00000280800
|
|
-1251.17742
|
468.102238
|
-20.49586
|
0
|
0
|
|
ENSG00000281181
|
|
-1251.17742
|
468.102238
|
-20.49586
|
0
|
0
|
|
ENSG00000165646
|
SLC18A2
|
25.24788
|
8.885919
|
19.54160
|
0
|
0
|
Table 5: Show the limma output after fitting the data to the organoid model, calculating the p-value, and correcting the p-value. This is only showing the first 10, ordered by p-value. Note that the p value and adjusted p values are not actually 0, they are just rounded to 0
(Isserlin 2022d; Xie 2021)
length(which(output_hits_org$P.Value < 0.05))
[1] 7838
(Isserlin 2022d)
length(which(output_hits_org$adj.P.Val < 0.05))
[1] 5677
(Isserlin 2022d)
Comparing both models
There is actually less, but let us look at it from a graph
simple_model_pvalues <- data.frame(ensembl_id =
output_hits$ensembl_gene_id,
simple_pvalue=output_hits$adj.P.Val)
org_model_pvalues <- data.frame(ensembl_id =
output_hits_org$ensembl_gene_id,
organoid_pvalue = output_hits_org$adj.P.Val)
two_models_pvalues <- merge(simple_model_pvalues,
org_model_pvalues,by.x=1,by.y=1)
two_models_pvalues$colour <- "black"
two_models_pvalues$colour[
two_models_pvalues$simple_pvalue<0.05] <- "orange"
two_models_pvalues$colour[
two_models_pvalues$organoid_pvalue<0.05] <- "blue"
two_models_pvalues$colour[
two_models_pvalues$simple_pvalue<0.05 &
two_models_pvalues$organoid_pvalue<0.05] <- "red"
plot(two_models_pvalues$simple_pvalue,
two_models_pvalues$organoid_pvalue,
col = two_models_pvalues$colour,
xlab = "Simple model corrected p-values",
ylab ="Organoid model corrected p-values",
main="Simple vs Organoid Limma",
xlim = c(0, 0.1),
ylim = c(0, 0.1))

Figure 3: Showing each gene on a graph where the coordinates is the corrected p-values using the simple model and the organoid model in limma, zoomed in on 0 to 0.1. Highlighting significant genes in both model as red, in simple model only as orange, in organoid model only as red, and non-significant genes as black (Isserlin 2022d)
To determine which model is more appropriate, we take a look at p-value of the gene of interest, CRX.
ensembl_of_interest <- normalized_count_data$ensembl_gene_id[
which(normalized_count_data$hgnc_symbol == "CRX")]
two_models_pvalues$colour <- "grey"
two_models_pvalues$colour[two_models_pvalues$ensembl_id==
ensembl_of_interest] <- "red"
plot(two_models_pvalues$simple_pvalue,
two_models_pvalues$organoid_pvalue,
col = two_models_pvalues$colour,
xlab = "simple model corrected p-values",
ylab ="organoid model corrected p-values",
main="Simple vs Organoid Limma",
xlim = c(0, 0.1),
ylim = c(0, 0.1))
points(two_models_pvalues[which(
two_models_pvalues$ensembl_id == ensembl_of_interest),2:3],
pch=20, col="red", cex=1.5)
legend(0,1,legend=c("CRX","rest"),
fill=c("red","grey"),cex = 0.7)

Figure 4: Showing each gene on a graph where the coordinates is the corrected p-values using the simple model and the organoid model in limma, zoomed in on 0 to 0.1. Highlighting only the gene of interest, CRX as red (Isserlin 2022d)
The simple model is better, I believe that is the because there are minimal difference between the retinal organoids, since they are derived from iPSC that is from the same patient.
Heatmap from limma
Let us look at the heatmap of only the top hits (by corrected pvalue), in the simple model
top_hits <- output_hits$ensembl_gene_id[
output_hits$adj.P.Val<0.05]
heatmap_matrix_tophits <- t(
scale(t(heatmap_matrix[
which(rownames(heatmap_matrix) %in% top_hits),])))
nrow(heatmap_matrix_tophits)
[1] 6174
if(min(heatmap_matrix_tophits) == 0){
heatmap_col = colorRamp2(c( 0, max(heatmap_atrix_tophits)),
c( "white", "red"))
} else {
heatmap_col = colorRamp2(c(min(heatmap_matrix_tophits), 0,
max(heatmap_matrix_tophits)), c("blue", "white", "red"))
}
Heatmap(as.matrix(heatmap_matrix_tophits[1:1000,]),
cluster_rows = TRUE,
cluster_columns = TRUE,
show_row_dend = TRUE,
show_column_dend = TRUE,
col=heatmap_col,
show_column_names = TRUE,
show_row_names = FALSE,
show_heatmap_legend = TRUE,
)

Figure 5: Heatmap for differential expression of the first 1000 genes, ordered by ensembl id, after row normalization for significant genes by corrected p-value using the simple model in limma. Only the first 1000 genes are shown due to rendering resource issues (Isserlin 2022d)
Let us not cluster the genes but group controls and experiments together
top_hits <- output_hits$ensembl_gene_id[
output_hits$adj.P.Val<0.05]
heatmap_matrix_tophits <- t(
scale(t(heatmap_matrix[
which(rownames(heatmap_matrix) %in% top_hits),])))
heatmap_matrix_tophits <- heatmap_matrix_tophits[,
c(
grep(colnames(heatmap_matrix_tophits), pattern = "\\CRXLCA"),
grep(colnames(heatmap_matrix_tophits), pattern = "\\Control")
)
]
nrow(heatmap_matrix_tophits)
[1] 6174
if(min(heatmap_matrix_tophits) == 0){
heatmap_col = colorRamp2(c( 0, max(heatmap_matrix_tophits)),
c( "white", "red"))
} else {
heatmap_col = colorRamp2(c(min(heatmap_matrix_tophits), 0,
max(heatmap_matrix_tophits)),
c("blue", "white", "red"))
}
Heatmap(as.matrix(heatmap_matrix_tophits[1:1000,]),
cluster_rows = TRUE,
cluster_columns = FALSE,
show_row_dend = TRUE,
show_column_dend = TRUE,
col=heatmap_col,
show_column_names = TRUE,
show_row_names = FALSE,
show_heatmap_legend = TRUE,
)

Figure 6: Heatmap for differential expression of the first 1000 genes, ordered by ensembl id, after row normalization for significant genes based on corrected p-value using the simple model in limma, without clustering, and group control and experiment together, respectively. Only the first 1000 genes are shown due to rendering resource issues (Isserlin 2022d)
We can clearly tell that the test condition do not look like each other. The controls also do not look like each other.
Then again look at top hits with less than 0.01 pvalue
top_hits <- output_hits$ensembl_gene_id[
output_hits$adj.P.Val < 0.01]
heatmap_matrix_tophits <- t(
scale(t(heatmap_matrix[which(rownames(heatmap_matrix) %in% top_hits),])))
heatmap_matrix_tophits <- heatmap_matrix_tophits[,
c(grep(colnames(heatmap_matrix_tophits),pattern = "\\CRXLCA"),
grep(colnames(heatmap_matrix_tophits),pattern = "\\Control"))]
nrow(heatmap_matrix_tophits)
if(min(heatmap_matrix_tophits) == 0){
heatmap_col = colorRamp2(c( 0, max(heatmap_matrix_tophits)),
c( "white", "red"))
} else {
heatmap_col = colorRamp2(c(min(heatmap_matrix_tophits), 0,
max(heatmap_matrix_tophits)),
c("blue", "white", "red"))
}
Heatmap(as.matrix(heatmap_matrix_tophits[1:1000,]),
cluster_rows = TRUE, show_row_dend = TRUE,
cluster_columns = FALSE,show_column_dend = FALSE,
col=heatmap_col,show_column_names = TRUE,
show_row_names = FALSE,show_heatmap_legend = TRUE)
Figure 7: Heatmap for differential expression of the first 1000 genes, ordered by ensembl id, after row normalization for hits with corrected p-value smaller than 0.01 using the simple model in limma, without clustering, and group control and experiment together, respectively. Only the first 1000 genes are shown due to rendering resource issues (Isserlin 2022d; Gu, Eils, and Schlesner 2016; Gu et al. 2014)
Heatmap with tophits from edgeR
Simple Model
The simple model is where the model accounts for the condition (control vs experiment), and the day that the data was collected on, but not account for which organoids is the data collected from.
filtered_data_matrix <- as.matrix(CRX_ExperimentRawCountFiltered[,3:28])
rownames(filtered_data_matrix) <- CRX_ExperimentRawCountFiltered$ens.id
d <- edgeR::DGEList(counts=filtered_data_matrix, group=samples$condition)
d <- edgeR::estimateDisp(d, model_design)
fit <- edgeR::glmQLFit(d, model_design)
(Isserlin 2022d; Chen, Lun, and Smyth 2016)
qlf.pos_vs_neg <- edgeR::glmQLFTest(fit, coef='samples$conditionCRXLCA')
knitr::kable(edgeR::topTags(qlf.pos_vs_neg), type="html",row.names = FALSE)
|
logFC
|
logCPM
|
F
|
PValue
|
FDR
|
|
2.911104
|
3.260820
|
2259.6310
|
0
|
0
|
|
4.776156
|
4.149346
|
1864.2162
|
0
|
0
|
|
-5.335662
|
1.951992
|
1626.9044
|
0
|
0
|
|
8.689098
|
1.065658
|
1399.1244
|
0
|
0
|
|
-3.289784
|
3.956177
|
998.1517
|
0
|
0
|
|
-3.903175
|
3.731926
|
782.7499
|
0
|
0
|
|
-1.658192
|
5.244965
|
608.3937
|
0
|
0
|
|
-2.892034
|
2.697657
|
582.5644
|
0
|
0
|
|
-6.520603
|
4.378680
|
568.5639
|
0
|
0
|
|
-3.219553
|
2.385684
|
530.9328
|
0
|
0
|
|
|
|
x
|
|
samples$conditionCRXLCA
|
|
|
(Isserlin 2022d; Chen, Lun, and Smyth 2016; Xie 2021)
Even though this say FDR, but since we are using the same multiple hypothesis testing method for both edgeR and limma, they are the same type of corrected values. The first one is gene before correction, and second one is after correction
qlf_output_hits <- edgeR::topTags(qlf.pos_vs_neg,sort.by = "PValue",
n = nrow(normalized_count_data))
length(which(qlf_output_hits$table$PValue < 0.05))
[1] 6909
length(which(qlf_output_hits$table$FDR < 0.05))
[1] 4652
(Isserlin 2022d; Chen, Lun, and Smyth 2016)
Organoid model
The organoid Model is where the model accounts for the condition (control vs experiment), and the day that the data was collected on, and for which organoids is the data collected from.
filtered_data_matrix <- as.matrix(CRX_ExperimentRawCountFiltered[,3:28])
rownames(filtered_data_matrix) <- CRX_ExperimentRawCountFiltered$ens.id
d <- edgeR::DGEList(counts=filtered_data_matrix, group=samples$condition)
d_org <- edgeR::estimateDisp(d, model_design_org)
fit_org <- edgeR::glmQLFit(d_org, model_design_org)
(Isserlin 2022d; Chen, Lun, and Smyth 2016)
qlf.pos_vs_neg <- edgeR::glmQLFTest(fit_org, coef='samples$conditionCRXLCA')
knitr::kable(edgeR::topTags(qlf.pos_vs_neg), type="html",row.names = FALSE)
|
logFC
|
logCPM
|
F
|
PValue
|
FDR
|
|
2.911666
|
3.260502
|
1983.9723
|
0
|
0
|
|
-5.334781
|
1.952266
|
1785.2164
|
0
|
0
|
|
4.779307
|
4.149185
|
1630.2907
|
0
|
0
|
|
8.689482
|
1.065724
|
1374.1628
|
0
|
0
|
|
-3.299374
|
3.956445
|
947.1039
|
0
|
0
|
|
-3.907342
|
3.732095
|
741.8239
|
0
|
0
|
|
-1.662314
|
5.244975
|
663.5731
|
0
|
0
|
|
-6.552258
|
4.378782
|
533.3569
|
0
|
0
|
|
-2.891894
|
2.697840
|
516.0534
|
0
|
0
|
|
-6.594928
|
-1.391819
|
495.8771
|
0
|
0
|
|
|
|
x
|
|
samples$conditionCRXLCA
|
|
|
(Isserlin 2022d; Xie 2021; Chen, Lun, and Smyth 2016)
The first one is gene before correction, and second one is after correction
qlf_output_hits_org <- edgeR::topTags(qlf.pos_vs_neg,sort.by = "PValue",
n = nrow(normalized_count_data))
length(which(qlf_output_hits_org$table$PValue < 0.05))
[1] 6556
length(which(qlf_output_hits_org$table$FDR < 0.05))
[1] 4222
(Isserlin 2022d; Chen, Lun, and Smyth 2016)
Compare 2 model from edgeR
simple_model_pvalues_qlf <- data.frame(ensembl_id =
rownames(qlf_output_hits$table),
simple_pvalue = qlf_output_hits$table$FDR)
org_model_pvalues_qlf <- data.frame(ensembl_id =
rownames(qlf_output_hits_org$table),
organoid_pvalue = qlf_output_hits_org$table$FDR)
two_models_pvalues <- merge(simple_model_pvalues_qlf,
org_model_pvalues_qlf, by.x=1, by.y=1)
two_models_pvalues$colour <- "black"
two_models_pvalues$colour[
two_models_pvalues$simple_pvalue<0.05] <- "orange"
two_models_pvalues$colour[
two_models_pvalues$organoid_pvalue<0.05] <- "blue"
two_models_pvalues$colour[
two_models_pvalues$simple_pvalue<0.05 &
two_models_pvalues$organoid_pvalue<0.05] <- "red"
plot(two_models_pvalues$simple_pvalue,
two_models_pvalues$organoid_pvalue,
col = two_models_pvalues$colour,
xlab = "Simple model corrected p-values",
ylab ="Organoid model corrected p-values",
main="Simple vs Organoid edgeR",
xlim=c(0, 0.1),
ylim=c(0, 0.1))

Figure 8: Showing each gene on a graph where the coordinates is the corrected p-values using the simple model and the organoid model in glmQLFit, zoomed in on 0-0.1. Highlighting significant genes in both model as red, in simple model only as orange, in organoid model only as red, and non-significant genes as black (Isserlin 2022d)
The majority of genes that have less than 0.05 p-value in either model, has less than 0.05 p-value in both model.
To determine which model is more appropriate, we take a look at p=value of the gene of interest, CRX.
ensembl_of_interest <- normalized_count_data$ensembl_gene_id[
which(normalized_count_data$hgnc_symbol == "CRX")]
two_models_pvalues$colour <- "grey"
two_models_pvalues$colour[two_models_pvalues$ensembl_id==
ensembl_of_interest] <- "red"
plot(two_models_pvalues$simple_pvalue,
two_models_pvalues$organoid_pvalue,
col = two_models_pvalues$colour,
xlab = "simple model corrected p-values",
ylab ="organoid model corrected p-values",
main="Simple vs Organoid Limma",
xlim = c(0, 0.01),
ylim = c(0, 0.01))
points(two_models_pvalues[which(
two_models_pvalues$ensembl_id == ensembl_of_interest),2:3],
pch=20, col="red", cex=1.5)
legend(0,1,legend=c("CRX","rest"),
fill=c("red","grey"),cex = 0.7)

Figure 9: Showing each gene on a graph where the coordinates is the corrected p-values using the simple model and the organoid model in glmQLFit, zoomed in on 0-0.01. Highlighting only the gene of interest, CRX (Isserlin 2022d)
The simple model is better, I believe this is still due to the same reason that, there are minimal difference between the retinal organoids, since they are derived from iPSC that is from the same patient.
Compare limma with quasi-likelihood
qlf_simple_model_pvalues <- data.frame(
ensembl_id = rownames(qlf_output_hits$table),
qlf_simple_pvalue = qlf_output_hits$table$FDR)
limma_simple_model_pvalues <- data.frame(
ensembl_id = output_hits$ensembl_gene_id,
limma_simple_pvalue = output_hits$adj.P.Val)
two_models_pvalues <- merge(qlf_pat_model_pvalues,
limma_pat_model_pvalues,
by.x=1,by.y=1)
two_models_pvalues$colour <- "black"
two_models_pvalues$colour[two_models_pvalues$qlf_simple_pvalue
<0.05] <- "orange"
two_models_pvalues$colour[two_models_pvalues$limma_simple_pvalue
<0.05] <- "blue"
two_models_pvalues$colour[two_models_pvalues$qlf_simple_pvalue
<0.05 &
two_models_pvalues$limma_simple_pvalue<0.05] <- "red"
plot(two_models_pvalues$qlf_simple_pvalue,
two_models_pvalues$limma_simple_pvalue,
col = two_models_pvalues$colour,
xlab = "QLF simple model adjusted p-values",
ylab ="Limma simple model adjusted p-values",
main="QLF vs Limma",
xlim = c(0, 1),
ylim = c(0, 1))

Figure 10: Showing each gene on a graph where the coordinates is the corrected p-values using the simple model in glmQLFit and in limma. Highlighting significant genes in both model as red, in simple model only as orange, in organoid model only as red, and non-significant genes as black (Isserlin 2022d)
For some reason, this is all over the place
ensembl_of_interest <- normalized_count_data$ensembl_gene_id[
which(normalized_count_data$hgnc_symbol == "CRX")]
two_models_pvalues$colour <- "grey"
two_models_pvalues$colour[two_models_pvalues$ensembl_id
==ensembl_of_interest] <- "red"
plot(two_models_pvalues$qlf_simple_pvalue,
two_models_pvalues$limma_simple_pvalue,
col = two_models_pvalues$colour,
xlab = "QLF simple model adjusted p-values",
ylab ="Limma simple model adjusted p-values",
main="QLF vs Limma",
xlim = c(0, 0.02),
ylim = c(0, 0.02))
points(two_models_pvalues[
two_models_pvalues$ensembl_id==ensembl_of_interest,2:3],
pch=24, col="red", cex=1.5)

Figure 11: Showing each gene on a graph where the coordinates is the corrected p-values using the simple model in glmQLFit and in limma, zoomed in on 0-0.02. Highlighting only the gene of interest, CRX (Isserlin 2022d)
It looks like QLF is better than limma, and that does make sense, since limma was made for microarray, but I am using bulk RNA-seq data, which edgeR methods are designed for.
Heatmap from edgeR
heatmap, but using tophits from qlf instead of limma
top_hits <- rownames(qlf_output_hits$table)[
qlf_output_hits$table$FDR<0.05]
heatmap_matrix_tophits <- t(
scale(t(heatmap_matrix[which(rownames(heatmap_matrix)
%in% top_hits),])))
nrow(heatmap_matrix_tophits)
[1] 4651
if(min(heatmap_matrix_tophits) == 0){
heatmap_col = colorRamp2(c( 0, max(heatmap_matrix_tophits)),
c( "white", "red"))
} else {
heatmap_col = colorRamp2(c(min(heatmap_matrix_tophits), 0,
max(heatmap_matrix_tophits)),
c("blue", "white", "red"))
}
Heatmap(as.matrix(heatmap_matrix_tophits[1:1000,]),
cluster_rows = TRUE,
cluster_columns = TRUE,
show_row_dend = TRUE,
show_column_dend = TRUE,
col=heatmap_col,
show_column_names = TRUE,
show_row_names = FALSE,
show_heatmap_legend = TRUE,
)

Figure 12: Heatmap for differential expression of the first 1000 genes, ordered by ensembl id, after row normalization for significant genes using the simple model in glmQLFit. Only the first 1000 genes are shown due to rendering resource issues (Isserlin 2022d; Gu, Eils, and Schlesner 2016; Gu et al. 2014)
compare to the heatmap from lemma, the day 200 do not cluster together, but the day 150 and day 125 cluster into 2 groups. However all day 90 do cluster together.
then also cluster by control vs experiment
top_hits <- rownames(qlf_output_hits$table)[qlf_output_hits$table$FDR
<0.05]
heatmap_matrix_tophits <- t(
scale(t(heatmap_matrix[which(rownames(heatmap_matrix)
%in% top_hits),])))
heatmap_matrix_tophits<- heatmap_matrix_tophits[,
c(grep(colnames(heatmap_matrix_tophits),pattern = "\\CRXLCA"),
grep(colnames(heatmap_matrix_tophits),pattern = "\\Control"))]
nrow(heatmap_matrix_tophits)
[1] 4651
if(min(heatmap_matrix_tophits) == 0){
heatmap_col = colorRamp2(c( 0, max(heatmap_matrix_tophits)),
c( "white", "red"))
} else {
heatmap_col = colorRamp2(c(min(heatmap_matrix_tophits), 0,
max(heatmap_matrix_tophits)),
c("blue", "white", "red"))
}
Heatmap(as.matrix(heatmap_matrix_tophits[1:1000,]),
cluster_rows = TRUE,
cluster_columns = FALSE,
show_row_dend = TRUE,
show_column_dend = FALSE,
col=heatmap_col,
show_column_names = TRUE,
show_row_names = FALSE,
show_heatmap_legend = TRUE)

Figure 13: Heatmap for differential expression of the first 1000 genes, ordered by ensembl id, after row normalization for significant genes using the simple model in glmQLFit, without clustering, and group control and experiment together, respectively. Only the first 1000 genes are shown due to rendering resource issues (Isserlin 2022d; Gu, Eils, and Schlesner 2016; Gu et al. 2014)
Over representation analysis
This is the number of genes upregulated and downregulated
length(which(qlf_output_hits$table$FDR < 0.05
& qlf_output_hits$table$logFC > 0))
[1] 2374
length(which(qlf_output_hits$table$FDR < 0.05
& qlf_output_hits$table$logFC < 0))
[1] 2278
(Isserlin 2022b)
I only outputted the first 5000 genes, because g:profiler will crash with too many genes.
qlf_output_hits_withgn <- merge(CRX_ExperimentRawCountFiltered[,1:2], qlf_output_hits, by.x=1, by.y = 0)
qlf_output_hits_withgn[,"rank"] <- -log(qlf_output_hits_withgn$FDR, base =10) * sign(qlf_output_hits_withgn$logFC)
qlf_output_hits_withgn <- qlf_output_hits_withgn[order(qlf_output_hits_withgn$rank),]
upregulated_genes <- qlf_output_hits_withgn$gene.id[
which(qlf_output_hits_withgn$FDR < 0.05
& qlf_output_hits_withgn$logFC > 0)]
downregulated_genes <- qlf_output_hits_withgn$gene.id[
which(qlf_output_hits_withgn$FDR < 0.05
& qlf_output_hits_withgn$logFC < 0)]
first5000_genes <- qlf_output_hits_withgn$gene.id[c(1:5000)]
if (!file.exists("data")) {
dir.create("data")
}
write.table(x=upregulated_genes,
file=file.path("data","CRX_upregulated_genes.txt"), sep = "\t",
row.names = FALSE, col.names = FALSE, quote = FALSE)
write.table(x=downregulated_genes,
file=file.path("data","CRX_downregulated_genes.txt"),sep = "\t",
row.names = FALSE, col.names = FALSE, quote = FALSE)
write.table(x=first5000_genes,
file=file.path("data","CRX_ranked_genelist.txt"),
sep = "\t",
row.names = FALSE,
col.names = FALSE,
quote = FALSE)
(Isserlin 2022b)
Discussion
We have 3 factors that we can include in our model design. The day that the day was collected, which organoid was it collected from, is it experiment or control that we are collecting from?
We do have to include the last one (experiment vs control), or else we are not getting the differential expression for the question that the authors is asking. (Kruczek et al. 2021). Although at first, the control and experiments do not differ that much, but by day 200 they the controls cluster together more than the organoids, so is the experiments. I believe we can conclude that, by day 200, the difference from CRX, is more than the organoid variability.
Looking at the MDS plot from A1, we can tell that the day it was collected from changes the data by a lot, since the RNA is taken from retinal organoids in development, and gene expression for cell in development changes from stage to stage. Therefore we should include this.
Still look at the MDS plot from A1, which organoid it was collected from does not make much of a difference, this is probably because that all control organoids are from the same person, and all experiment organoids are from the same person.
LS0tCnRpdGxlOiAnQkNCNDIwIEFzc2lnbm1lbnQgMiBSZXBvcnQ6IERpZmZlcmVudGlhbCBHZW5lIGV4cHJlc3Npb24gYW5kIFByZWxpbWluYXJ5IE9SQScKYXV0aG9yOiAiS2FpIFJlbiBDaGVuIgpzdWJ0aXRsZTogJ0pvdXJuYWwgQXJ0aWNsZTogR2VuZSB0aGVyYXB5IG9mIGRvbWluYW50IENSWC1MZWJlciBjb25nZW5pdGFsIGFtYXVyb3NpcwogIHVzaW5nIHBhdGllbnQgcmV0aW5hbCBvcmdhbm9pZHMnCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogMgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAnMicKICAgIGRmX3ByaW50OiBwYWdlZApiaWJsaW9ncmFwaHk6IGEyY2l0YXRpb24uYmliCmxpbmstY2l0YXRpb25zOiB5ZXMKLS0tCgojIEludHJvZHVjdGlvbgpjb25lIHJvZCBob21lb2JveCAoQ1JYKSBtdXRhdGlvbnMgIGNhbiBjYXVzZSBkb21pbmFudCBMZWJlciBjb25nZW5pdGFsIGFtYXVyb3NpcwooTENBKSwgYSBnZW5ldGljIGRpc29yZGVyIHRoYXQgY2FuIGxlYWQgdG8gYmxpbmRuZXNzLiBOb3JtYWxseSBMQ0EgaXMgY2F1c2VkCmJ5IHJlY2Vzc2l2ZSBnZW5lcywgYnV0IG9uIHJhcmVyIGNhc2VzLCBpdCBjYW4gYmUgY2F1c2UgYnkgZG9taW5hbnQgZ2VuZXMsIHN1Y2gKYXMgQ1JYLiBXZSBhcmUgbG9vayBhdCBCdWxrIFJOQS1zZXEgZGF0YSBmcm9tIHJldGluYWwgb3JnYW5vaWRzIGRlcml2ZWQgZnJvbQppbmR1Y2VkIHBsdXJpcG90ZW50IHN0ZW0gY2VsbHMgKGlQU0NzKSB3aGljaCBpcyBpdHNlbGYgdGFrZSBmcm9tIGEgTENBIHBhdGllbnQgCndpdGggQ1JYLUkxMzhmcyBtdXRhdGlvbiBhbmQgdGhlaXIgaGVhbHRoeSBwYXJlbnQuIFRoaXMgbXV0YXRpb24gb2NjdXJzIGluIHRoZQp0cmFuc2FjdGl2YXRpb24gZG9tYWluLCBhbmQgc28gdGhlIGVmZmVjdCBvZiB0aGUgbXV0YXRpb24gaXN0IHRoYXQgQ1JYIGlzIG5vdAp0cmFuc2NyaWJlZC4gVGhlIGRhdGEgc291cmNlIGlzIEdTRTE1MjkzOS4gVGhlIGF1dGhvciBpcyB0cnlpbmcgdG8gc2hvdyB0aGF0IGEKZGVuby1hc3NvY2lhdGVkIHZpcnVzIGdlbmUgdGhlcmFweSBjYW4gcmVzdG9yZSBSTkEgZXhwcmVzc2lvbiBvZiBDUlguIEhvd2V2ZXIsCmFsdGhvdWdoIHRoaXMgc3R1ZHkgaXMgYWJvdXQgZ2VuZSB0aGVyYXB5LCB0aGUgZGF0YSB0aGF0IHNob3cgcmVzY3VlZCBleHByZXNzaW9uCmlzIGluIHNpbmdsZSBjZWxsIFJOQS1zZXEgZGF0YSwgd2hpY2ggaXMgbm90IGluIHRoZSBzY29wZSBvZiB0aGlzIHJlcG9ydCwgc28gaXQKd2FzIG5vdCBpbmNsdWRlZC4gVGhlIGRhdGEgd2FzIGNvbGxlY3RlZCBmcm9tIDYgcmV0aW5hbCBvcmdhbm9pZHMsIGZvciA5MCBkYXlzLAoxMjUgZGF5cywgMTUwIGRheXMgYW5kIDIwMCBkYXkgYWZ0ZXIgdGhlIGlQU0NzIHN0YXJ0IHRvIGRpZmZlcmVudGlhdGU7IDMgb2YgdGhlCm9yZ2Fub2lkcyB3ZXJlIGZyb20gTENBIHBhdGllbnQsIGFuZCAzIG9yZ2Fub2lkcyB3ZXJlIGZyb20gdGhlaXIgaGVhbHRoeSBwYXJlbnQuCltAQ1JYXQoKTm90ZSB0aGF0IHRoZSB2YXN0IG1ham9yaXR5IG9mIGNvZGUgaW4gdGhpcyByZXBvcnQgYXJlIHRha2UgZnJvbSBCQ0I0MjAgbGVjdHVyZQpbQGw0OyBAbDU7IEBsNjsgQGw3XSB3aXRoIG1pbm9yIG1vZGlmaWNhdGlvbiB0byBtYWtlIGl0IGFwcHJvcHJpYXRlIGZvciBteSBkYXRhLgoKTm90ZSAyOiBJIGNhbm5vdCBwdXQgYmlidGV4IGlubGluZSBjaXRhdGF0aW9uIHdpdGhpbiB0aGUgY29kZWNodW5rLCBzbyBpdCBpcyBsZWZ0CmF0IHRoZSBlbmQgb2YgdGhlIGNvZGUgY2h1bmssIGFuZCBzb21ldGltZXMgdGhlcmUgaXMgYSBmaWd1cmUsIHNvIHRoZSBmaWd1cmUKbGVnZW5kL2NhcHRpb24gbWF5IGJlIGRpcmVjdGx5IGZvbGxvd2VkIGJ5IHRoZSBpbmxpbmUgY2l0YXRpb24KCiMgQXNzaWdubWVudCAxIHByb2Nlc3NpbmcgCgpMZXRzIHVzIGZpcnN0IGRvIGFsbCB0aGUgcHJvY2Vzc2luZyB0aGF0IHdhcyBkb25lIGluIGFzc2lnbm1lbnQgMQoKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnNvdXJjZSgiLi9hc3NpZ25tZW50X2Z1bmN0aW9ucy5yIikKc3VwcGxlbWVudGFyeUZpbGVzIDwtIGRvd25sb2FkU3VwcGxlbWVudGFyeUZpbGVzKCJHU0UxNTI5MzkiKQpDUlhfRXhwZXJpbWVudFJhd0NvdW50IDwtIHJlYWRGaWxlKHN1cHBsZW1lbnRhcnlGaWxlcykKQ1JYX0V4cGVyaW1lbnRSYXdDb3VudEZpbHRlcmVkIDwtIGZpbHRlck91dExvd0NvdW50KENSWF9FeHBlcmltZW50UmF3Q291bnQpCm5vcm1hbGl6ZWRfY291bnRzIDwtIG5vcm1hbGl6ZVRoZUNvdW50cyhDUlhfRXhwZXJpbWVudFJhd0NvdW50RmlsdGVyZWQpCmBgYApbQGw0OyBAbDU7IEBnZW9xdWVyeTsgQGJpb21hcnQ7IEBlZGdlcl0KCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmVuc2VtYmxEYXRhU2V0IDwtIGdldEVuc2VtYmxiaW9tYXJ0KCkKYGBgCltAbDQ7IEBsNTsgQGJpb21hcnRdCgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0Kbm9ybWFsaXplZF9jb3VudHNfYW5ub3QgPC0gbWFwVGhlRGF0YShDUlhfRXhwZXJpbWVudFJhd0NvdW50RmlsdGVyZWQsIG5vcm1hbGl6ZWRfY291bnRzLCBlbnNlbWJsRGF0YVNldCkKYGBgCltAbDQ7IEBsNTsgQGJpb21hcnRdCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpzYXZlUkRTKG5vcm1hbGl6ZWRfY291bnRzX2Fubm90LCBmaWxlID0gIm5vcm1hbGl6ZWRfY291bnRzX2Fubm90LnJkcyIpCmBgYAoKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgQXBwcmVudGx5IHRoZXJlIGlzIGEgZHVwbGlhdGUgZW1zZWJsZW0gaWQKbm9ybWFsaXplZF9jb3VudF9kYXRhIDwtIG5vcm1hbGl6ZWRfY291bnRzX2Fubm90WyFkdXBsaWNhdGVkKG5vcm1hbGl6ZWRfY291bnRzX2Fubm90WyAsIGMoImVuc2VtYmxfZ2VuZV9pZCIpXSksXQpgYGAKCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSdUYWJsZSAxYTogVGhlIGR1cGxpY2F0ZSBnZW5lIHRoYXQgcmVtb3ZlZC4nfQpub3JtYWxpemVkX2NvdW50c19hbm5vdFtkdXBsaWNhdGVkKG5vcm1hbGl6ZWRfY291bnRzX2Fubm90WyAsIGMoImVuc2VtYmxfZ2VuZV9pZCIpXSksXQoKYGBgClRhYmxlIDFhOiBUaGUgZHVwbGljYXRlIGdlbmUgdGhhdCB3YXMgcmVtb3ZlZC4KCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSdUYWJsZSAxYjogc2hvd2luZyB0aGUgdHdvIGdlbmUgaGFzIHRoZSBzYW1lIGVuc2VtYmwgZ2VuZSBpZCwgYnV0IG5vdCBleGFjdGx5IHRoZSBzYW1lIGhnbmMgc3ltYm9sJ30Kbm9ybWFsaXplZF9jb3VudHNfYW5ub3Rbd2hpY2gobm9ybWFsaXplZF9jb3VudHNfYW5ub3QkZW5zZW1ibF9nZW5lX2lkID09ICdFTlNHMDAwMDAyNTQ4NzYnKSwgXQpgYGAKVGFibGUgMWI6IHNob3dpbmcgdGhlIHR3byBnZW5lIGhhcyB0aGUgc2FtZSBlbnNlbWJsIGdlbmUgaWQsIGJ1dCBub3QgZXhhY3RseSB0aGUgc2FtZSBoZ25jIHN5bWJvbAoKSSBmaW5kIHRoZSBvbmUgdGhhdCBpcyBjb25zaWRlciBhcyBkdXBsaWNhdGUsIHRoZW4gZmluZCB0aGUgb25lIHRoYXQgaGFzIHRoZSBzYW1lIGVubXNlbGJsZSBnZW5lIGlkIGFzIGl0LiAKRm9yIHNvbWUgcmVhc29uIHRoaXMgZW5zZW1ibCBnZW5lIGlkIG1hcHMgdG8gMiBoZ25jIHN5bWJvbHMsIFNVR1QxUDQtU1RSQTZMUCBhbmQgU1RSQTZMUCwgZXZlbiBmcm9tIHRoZSBuYW1lIGl0IGxvb2sgbGlrZSB0aGUgc2FtZSBnZW5lCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpub3JtYWxpemVkX2NvdW50X2NsZWFuZWQgPC0gbm9ybWFsaXplZF9jb3VudF9kYXRhW25vcm1hbGl6ZWRfY291bnRfZGF0YSRlbnNlbWJsX2dlbmVfaWQgIT0gJ0VOU0cwMDAwMDE1NjUwOCcsIF0KYGBgCgoKCiMgSGVhdG1hcCBmb3IgYWxsIGdlbmVzCgpmaXJzdCBtYWtlIGEgaGVhdCBtYXAgbWF0cml4ICh0aGUgaW5wdXQgdGhpbmcgdXNlIHRvIGdlbmVyYXRlIGEgaGVhdCBtYXApCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCBmaWcuY2FwPSdUYWJsZSAyOiBUaGUgZmlyc3QgMTAwMCByb3cgb2YgdGhlIGZpbHRlcmVkIG5vcm1hbGl6ZWQgZGF0YSBmb3IgYWxsIHNhbXBsZXMsIG9yZGVyZWQgYnkgZW5zZW1ibCBpZCcsIHdhcm5pbmc9RkFMU0V9CgpoZWF0bWFwX21hdHJpeCA8LSBub3JtYWxpemVkX2NvdW50X2NsZWFuZWRbLAogICAgICAgICAgICAgICAgICAgICAgICAzOm5jb2wobm9ybWFsaXplZF9jb3VudF9jbGVhbmVkKV0Kcm93bmFtZXMoaGVhdG1hcF9tYXRyaXgpIDwtIG5vcm1hbGl6ZWRfY291bnRfY2xlYW5lZCRlbnNlbWJsX2dlbmVfaWQKY29sbmFtZXMoaGVhdG1hcF9tYXRyaXgpIDwtIGNvbG5hbWVzKG5vcm1hbGl6ZWRfY291bnRfY2xlYW5lZFssIDM6bmNvbChub3JtYWxpemVkX2NvdW50X2NsZWFuZWQpXSkKaGVhdG1hcF9tYXRyaXgKc2F2ZVJEUyhoZWF0bWFwX21hdHJpeCwgZmlsZSA9ICJoZWF0bWFwX2xpc3QucmRzIikKYGBgClRhYmxlIDI6IFRoZSBmaXJzdCAxMDAwIHJvdyBvZiB0aGUgZmlsdGVyZWQgbm9ybWFsaXplZCBkYXRhIGZvciBhbGwgc2FtcGxlcywgb3JkZXJlZCBieSBlbnNlbWJsIGlkCgpbQGw2XQoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KaGVhdG1hcF9tYXRyaXggPC0gcmVhZFJEUygiLi9oZWF0bWFwX2xpc3QucmRzIikKYGBgCgoKdGhlbiBnZW5lcmF0ZSBhIGhlYXQgbWFwCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSdGaWd1cmUgMTogSW5pdGlhbCBoZWF0bWFwIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiB3aXRoIHRoZSBmaXJzdCAxMDAwIGdlbmVzLCBvcmRlciBieSBlbnNlbWJsIGlkLiBPbmx5IHRoZSBmaXJzdCAxMDAwIGdlbmVzIGFyZSBzaG93biBkdWUgdG8gcmVuZGVyaW5nIHJlc291cmNlIGlzc3Vlcyd9CmxpYnJhcnkoQ29tcGxleEhlYXRtYXApCmxpYnJhcnkoY2lyY2xpemUpCmlmKG1pbihoZWF0bWFwX21hdHJpeCkgPT0gMCl7CiAgaGVhdG1hcF9jb2wgPSBjb2xvclJhbXAyKGMoMCwgbWF4KGhlYXRtYXBfbWF0cml4KSksIGMoICJ3aGl0ZSIsICJyZWQiKSkKCn0gZWxzZSB7CiAgaGVhdG1hcF9jb2wgPSBjb2xvclJhbXAyKGMobWluKGhlYXRtYXBfbWF0cml4KSwgMCwgbWF4KGhlYXRtYXBfbWF0cml4KSksIGMoImJsdWUiLCAid2hpdGUiLCAicmVkIikpCn0KCkhlYXRtYXAoYXMubWF0cml4KGhlYXRtYXBfbWF0cml4WzE6MTAwMCxdKSwKICAgICAgc2hvd19yb3dfZGVuZCA9IFRSVUUsIHNob3dfY29sdW1uX2RlbmQgPSBUUlVFLAogICAgICBjb2w9aGVhdG1hcF9jb2wsIHNob3dfY29sdW1uX25hbWVzID0gVFJVRSwKICAgICAgc2hvd19yb3dfbmFtZXMgPSBGQUxTRSwgc2hvd19oZWF0bWFwX2xlZ2VuZCA9IFRSVUUsCiAgICAgIHJhc3Rlcl9yZXNpemVfbWF0ID0gVFJVRSkKYGBgCkZpZ3VyZSAxOiBJbml0aWFsIGhlYXRtYXAgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHdpdGggdGhlIGZpcnN0IDEwMDAgZ2VuZXMsIG9yZGVyIGJ5IGVuc2VtYmwgaWQuIE9ubHkgdGhlIGZpcnN0IDEwMDAgZ2VuZXMgYXJlIHNob3duIGR1ZSB0byByZW5kZXJpbmcgcmVzb3VyY2UgaXNzdWVzCltAbDY7IEBoZWF0bWFwOyBAY29sb3JdCgoKCk5vdyB3aXRoIHJvdyBub3JtYWxpemF0aW9uIChpLmUuIHRoZSBraW5kIHdpdGggc3VidHJhY3QgdGhlIG1lYW4gZGl2aWRlZCBieSBzdGFuZGFyZCBkZXZpYXRpb24pCndlIGNhbiBzZWUgZWFjaCBnZW5lIGJldHRlcgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmNhcD0nRmlndXJlIDI6IEhlYXRtYXAgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIG9mIHRoZSBmaXJzdCAxMDAwIGdlbmVzLCBvcmRlcmVkIGJ5IGVuc2VtYmwgaWQsIGFmdGVyIHJvdyBub3JtYWxpemF0aW9uLiBPbmx5IHRoZSBmaXJzdCAxMDAwIGdlbmVzIGFyZSBzaG93biBkdWUgdG8gcmVuZGVyaW5nIHJlc291cmNlIGlzc3Vlcyd9CmhlYXRtYXBfbWF0cml4IDwtIHQoc2NhbGUodChoZWF0bWFwX21hdHJpeCkpKQoKaWYobWluKGhlYXRtYXBfbWF0cml4KSA9PSAwKXsKaGVhdG1hcF9jb2wgPSBjb2xvclJhbXAyKGMoMCwgbWF4KGhlYXRtYXBfbWF0cml4KSksIGMoICJ3aGl0ZSIsICJyZWQiKSkKfSBlbHNlIHsKaGVhdG1hcF9jb2wgPSBjb2xvclJhbXAyKGMobWluKGhlYXRtYXBfbWF0cml4KSwgMCwgbWF4KGhlYXRtYXBfbWF0cml4KSksIGMoImJsdWUiLCAid2hpdGUiLCAicmVkIikpCn0KCkhlYXRtYXAoYXMubWF0cml4KGhlYXRtYXBfbWF0cml4WzE6MTAwMCxdKSwKICAgICAgc2hvd19yb3dfZGVuZCA9IFRSVUUsc2hvd19jb2x1bW5fZGVuZCA9IFRSVUUsCiAgICAgIGNvbD1oZWF0bWFwX2NvbCxzaG93X2NvbHVtbl9uYW1lcyA9IFRSVUUsCiAgICAgIHNob3dfcm93X25hbWVzID0gRkFMU0Usc2hvd19oZWF0bWFwX2xlZ2VuZCA9IFRSVUUpCmBgYApGaWd1cmUgMjogSGVhdG1hcCBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gb2YgdGhlIGZpcnN0IDEwMDAgZ2VuZXMsIG9yZGVyZWQgYnkgZW5zZW1ibCBpZCwgYWZ0ZXIgcm93IG5vcm1hbGl6YXRpb24uIE9ubHkgdGhlIGZpcnN0IDEwMDAgZ2VuZXMgYXJlIHNob3duIGR1ZSB0byByZW5kZXJpbmcgcmVzb3VyY2UgaXNzdWVzCltAbDY7IEBoZWF0bWFwOyBAY29sb3JdCgp5b3UgY2FuIHNlZSB0aGF0IHRoZSBzYW1wbGVzIGFyZSBtb3JlIHNpbWlsYXIgdG8gZWFjaCBvdGhlciBhdCB0aGUgc2FtZSB0aW1lIHBvaW50IHRoYW4gd2hldGhlciBvciBub3QgaXQgaXMgY29udHJvbCwgSSB0aGluayB0aGF0IGlzIHJlYXNvbmFibGUgc2luY2UgbWFueSBnZW5lIGdldCBzaW11bGF0ZWQgYW5kIHJlcHJlc3NlZCBkdXJpbmcgZGV2ZWxvcG1lbnQsIHNvIHRoZSBnZW5lcyB0aGF0IGFyZSBvbiBpcyBkaWZmZXJlbnQgZnJvbSBvbmUgZGV2ZWxvcG1lbnQgdGltZSBwb2ludCwgdG8gYW5vdGhlci4gCgojIEhlYXRtYXAgZm9yIHRvcGhpdHMgdXNpbmcgTGltbWEKCldlIHdpbGwgYmUgZml0dGluZyB0aGUgZGF0YSB0byB0aGUgYSBsaW5lYXIgbW9kZWwgYW5kIHVzaW5nIGVtcGlyaWNhbCBiYXllcyB0byAKY2FsY3VsYXRlIHAtdmFsdWVzLiBUaGVuIHdlIHdpbGwgdXNpbmcgQmVuamFtbmktSG9jaGJlcmcgZm9yIG11bHRpcGxlIGh5cG90aGVzaXMKdGVzdGluZywgZm9yIGNvcnJlY3Rpbmcgb3VyIHAtdmFsdWVzLgoKIyMgU2ltcGxlIE1vZGVsCgpUaGUgc2ltcGxlIG1vZGVsIGlzIHdoZXJlIHRoZSBtb2RlbCBhY2NvdW50cyBmb3IgdGhlIGNvbmRpdGlvbiAoY29udHJvbCB2cyAKZXhwZXJpbWVudCksIGFuZCB0aGUgZGF5IHRoYXQgdGhlIGRhdGEgd2FzIGNvbGxlY3RlZCBvbiwgYnV0IG5vdCBhY2NvdW50IApmb3Igd2hpY2ggb3JnYW5vaWRzIGlzIHRoZSBkYXRhIGNvbGxlY3RlZCBmcm9tLiAKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5jYXA9J1RhYmxlIDM6IFNob3dpbmcgZm9yIGVjYWggc2FtcGxlLCB3aGljaCBvcmdhbm9pZCwgYXQgd2hhdCB0aW1lLCBhbmQgd2hhdCBjb25kaXRpb24gaXQgaXMnfQpzYW1wbGVzIDwtIGRhdGEuZnJhbWUobGFwcGx5KGNvbG5hbWVzKENSWF9FeHBlcmltZW50UmF3Q291bnRGaWx0ZXJlZClbMzpuY29sKENSWF9FeHBlcmltZW50UmF3Q291bnRGaWx0ZXJlZCldLCBGVU49ZnVuY3Rpb24oeCl7dW5saXN0KHN0cnNwbGl0KHgsIHNwbGl0ID0gIlxcXyIpKVtjKDEsIDIsIDMpXX0pKQpzYW1wbGVzCmNvbG5hbWVzKHNhbXBsZXMpIDwtIGNvbG5hbWVzKENSWF9FeHBlcmltZW50UmF3Q291bnRGaWx0ZXJlZClbMzpuY29sKENSWF9FeHBlcmltZW50UmF3Q291bnRGaWx0ZXJlZCldCnJvd25hbWVzKHNhbXBsZXMpIDwtIGMoImNvbmRpdGlvbiIsICJ0aW1lIiwicGF0aWVudCIpCnNhbXBsZXMgPC0gZGF0YS5mcmFtZSh0KHNhbXBsZXMpKQoKbW9kZWxfZGVzaWduIDwtIG1vZGVsLm1hdHJpeCh+IHNhbXBsZXMkY29uZGl0aW9uK3NhbXBsZXMkdGltZSkKCmV4cHJlc3Npb25NYXRyaXggPC0gYXMubWF0cml4KG5vcm1hbGl6ZWRfY291bnRfZGF0YVssMzpuY29sKENSWF9FeHBlcmltZW50UmF3Q291bnRGaWx0ZXJlZCldKQpyb3duYW1lcyhleHByZXNzaW9uTWF0cml4KSA8LQogIG5vcm1hbGl6ZWRfY291bnRfZGF0YSRlbnNlbWJsX2dlbmVfaWQKY29sbmFtZXMoZXhwcmVzc2lvbk1hdHJpeCkgPC0KICBjb2xuYW1lcyhub3JtYWxpemVkX2NvdW50X2RhdGEpWzM6bmNvbChDUlhfRXhwZXJpbWVudFJhd0NvdW50RmlsdGVyZWQpXQptaW5pbWFsU2V0IDwtIEJpb2Jhc2U6OkV4cHJlc3Npb25TZXQoYXNzYXlEYXRhPWV4cHJlc3Npb25NYXRyaXgpCgpmaXQgPC0gbGltbWE6OmxtRml0KG1pbmltYWxTZXQsIG1vZGVsX2Rlc2lnbikKZml0MiA8LSBsaW1tYTo6ZUJheWVzKGZpdCx0cmVuZD1UUlVFKQoKdG9wZml0IDwtIGxpbW1hOjp0b3BUYWJsZShmaXQyLAogICAgICAgICAgICAgICAgICAgY29lZj1uY29sKG1vZGVsX2Rlc2lnbiksCiAgICAgICAgICAgICAgICAgICBhZGp1c3QubWV0aG9kID0gIkJIIiwKICAgICAgICAgICAgICAgICAgIG51bWJlciA9IG5yb3coZXhwcmVzc2lvbk1hdHJpeCkpCiNtZXJnZSBoZ25jIG5hbWVzIHRvIHRvcGZpdCB0YWJsZQpvdXRwdXRfaGl0cyA8LSBtZXJnZShub3JtYWxpemVkX2NvdW50X2RhdGFbLDE6Ml0sCiAgICAgICAgICAgICAgICAgICAgIHRvcGZpdCwKICAgICAgICAgICAgICAgICAgICAgYnkueT0wLGJ5Lng9MSwKICAgICAgICAgICAgICAgICAgICAgYWxsLnk9VFJVRSkKI3NvcnQgYnkgcHZhbHVlCm91dHB1dF9oaXRzIDwtIG91dHB1dF9oaXRzW29yZGVyKG91dHB1dF9oaXRzJFAuVmFsdWUpLF0KYGBgClRhYmxlIDM6IFNob3dpbmcgZm9yIGVjYWggc2FtcGxlLCB3aGljaCBvcmdhbm9pZCwgYXQgd2hhdCB0aW1lLCBhbmQgd2hhdCBjb25kaXRpb24gaXQgaXMKCltAbDY7IEBiaW9iYXNlOyBAbGltbWFdCgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmNhcD0nVGFibGUgNDogU2hvdyB0aGUgbGltbWEgb3V0cHV0IGFmdGVyIGZpdHRpbmcgdGhlIGRhdGEgdG8gdGhlIHNpbXBsZSBtb2RlbCwgY2FsY3VsYXRpbmcgdGhlIHAtdmFsdWUsIGFuZCBjb3JyZWN0aW5nIHRoZSBwLXZhbHVlLiBUaGlzIGlzIG9ubHkgc2hvd2luZyB0aGUgZmlyc3QgMTAsIG9yZGVyZWQgYnkgcCB2YWx1ZS4gTm90ZSB0aGF0IHRoZSBwIHZhbHVlIGFuZCBhZGp1c3RlZCBwIHZhbHVlcyBhcmUgbm90IGFjdHVhbGx5IDAsIHRoZXkgYXJlIGp1c3Qgcm91bmRlZCB0byAwJ30Ka25pdHI6OmthYmxlKG91dHB1dF9oaXRzWzE6MTAsMTo3XSx0eXBlPSJodG1sIixyb3cubmFtZXMgPSBGQUxTRSkKYGBgClRhYmxlIDQ6IFNob3cgdGhlIGxpbW1hIG91dHB1dCBhZnRlciBmaXR0aW5nIHRoZSBkYXRhIHRvIHRoZSBzaW1wbGUgbW9kZWwsIGNhbGN1bGF0aW5nIHRoZSBwLXZhbHVlLCBhbmQgY29ycmVjdGluZyB0aGUgcC12YWx1ZS4gVGhpcyBpcyBvbmx5IHNob3dpbmcgdGhlIGZpcnN0IDEwLCBvcmRlcmVkIGJ5IHAgdmFsdWUuIE5vdGUgdGhhdCB0aGUgcCB2YWx1ZSBhbmQgYWRqdXN0ZWQgcCB2YWx1ZXMgYXJlIG5vdCBhY3R1YWxseSAwLCB0aGV5IGFyZSBqdXN0IHJvdW5kZWQgdG8gMAoKW0BsNjsgQGtuaXRyXQoKVGhpcyBpcyBob3cgbWFueSBnZW5lcyB0aGF0IGhhdmUgcHZhbHVlIHVuZGVyIDAuMDUKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9Cmxlbmd0aCh3aGljaChvdXRwdXRfaGl0cyRQLlZhbHVlIDwgMC4wNSkpCmBgYApbQGw2XQoKClRoaXMgaXMgaG93IG1hbnkgZ2VuZXMgdGhhdCBzdGlsbCBwdmFsdWUgb2YgbGVzcyB0aGFuIDAuMDUgYWZ0ZXIgYWRqdXN0bWVudApgYGB7ciB3YXJuaW5nPUZBTFNFfQpsZW5ndGgod2hpY2gob3V0cHV0X2hpdHMkYWRqLlAuVmFsIDwgMC4wNSkpCmBgYApbQGw2XQoKCiMjIE9yZ2Fub2lkIE1vZGVsCgpUaGUgb3JnYW5vaWQgbW9kZWwgaXMgd2hlcmUgdGhlIG1vZGVsIGFjY291bnRzIGZvciB0aGUgY29uZGl0aW9uIChjb250cm9sIHZzIApleHBlcmltZW50KSwgYW5kIHRoZSBkYXkgdGhhdCB0aGUgZGF0YSB3YXMgY29sbGVjdGVkIG9uLCBhbmQgCmZvciB3aGljaCBvcmdhbm9pZHMgaXMgdGhlIGRhdGEgY29sbGVjdGVkIGZyb20uIAoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbW9kZWxfZGVzaWduX29yZyA8LSBtb2RlbC5tYXRyaXgoCiAgfiBzYW1wbGVzJHBhdGllbnQgKyBzYW1wbGVzJGNvbmRpdGlvbiArIHNhbXBsZXMkdGltZSkKZml0X3BhdCA8LSBsaW1tYTo6bG1GaXQobWluaW1hbFNldCwgbW9kZWxfZGVzaWduX29yZykKYGBgCltAbDY7IEBrbml0cl0KCkNoYW5nZSB0aGUgbW9kZWwsIGZpdCB0aGUgZGF0YSB0byB0aGUgbW9kZWwsIHRoZW4gYXBwbHkgZW1waXJjYWwgYmF5ZXMgLCAKdGhlbiBjb3JyZWN0IHdpdGggQkgKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmZpdF9vcmcgPC0gbGltbWE6OmxtRml0KG1pbmltYWxTZXQsIG1vZGVsX2Rlc2lnbl9vcmcpCmZpdDJfb3JnIDwtIGxpbW1hOjplQmF5ZXMoZml0X29yZyx0cmVuZD1UUlVFKQoKdG9wZml0X29yZyA8LSBsaW1tYTo6dG9wVGFibGUoZml0Ml9vcmcsCiAgICAgICAgICAgICAgICAgICBjb2VmPW5jb2wobW9kZWxfZGVzaWduX29yZyksCiAgICAgICAgICAgICAgICAgICBhZGp1c3QubWV0aG9kID0gIkJIIiwKICAgICAgICAgICAgICAgICAgIG51bWJlciA9IG5yb3coZXhwcmVzc2lvbk1hdHJpeCkpCgojbWVyZ2UgaGduYyBuYW1lcyB0byB0b3BmaXQgdGFibGUKb3V0cHV0X2hpdHNfb3JnIDwtIG1lcmdlKG5vcm1hbGl6ZWRfY291bnRfZGF0YVssMToyXSwKICAgICAgICAgICAgICAgICAgICAgICAgIHRvcGZpdF9vcmcsIGJ5Lnk9MCwgYnkueD0xLCBhbGwueT1UUlVFKQojc29ydCBieSBwdmFsdWUKb3V0cHV0X2hpdHNfb3JnIDwtIG91dHB1dF9oaXRzX29yZ1tvcmRlcihvdXRwdXRfaGl0c19vcmckUC5WYWx1ZSksXQoKYGBgCltAbDY7IEBsaW1tYV0KCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSdUYWJsZSA1OiBTaG93IHRoZSBsaW1tYSBvdXRwdXQgYWZ0ZXIgZml0dGluZyB0aGUgZGF0YSB0byB0aGUgb3JnYW5vaWQgbW9kZWwsIGNhbGN1bGF0aW5nIHRoZSBwLXZhbHVlLCBhbmQgY29ycmVjdGluZyB0aGUgcC12YWx1ZS4gVGhpcyBpcyBvbmx5IHNob3dpbmcgdGhlIGZpcnN0IDEwLCBvcmRlcmVkIGJ5IHAtdmFsdWUuIE5vdGUgdGhhdCB0aGUgcCB2YWx1ZSBhbmQgYWRqdXN0ZWQgcCB2YWx1ZXMgYXJlIG5vdCBhY3R1YWxseSAwLCB0aGV5IGFyZSBqdXN0IHJvdW5kZWQgdG8gMCd9CmtuaXRyOjprYWJsZShvdXRwdXRfaGl0c19vcmdbMToxMCwxOjddLHR5cGU9Imh0bWwiLHJvdy5uYW1lcyA9IEZBTFNFKQpgYGAKVGFibGUgNTogU2hvdyB0aGUgbGltbWEgb3V0cHV0IGFmdGVyIGZpdHRpbmcgdGhlIGRhdGEgdG8gdGhlIG9yZ2Fub2lkIG1vZGVsLCBjYWxjdWxhdGluZyB0aGUgcC12YWx1ZSwgYW5kIGNvcnJlY3RpbmcgdGhlIHAtdmFsdWUuIFRoaXMgaXMgb25seSBzaG93aW5nIHRoZSBmaXJzdCAxMCwgb3JkZXJlZCBieSBwLXZhbHVlLiBOb3RlIHRoYXQgdGhlIHAgdmFsdWUgYW5kIGFkanVzdGVkIHAgdmFsdWVzIGFyZSBub3QgYWN0dWFsbHkgMCwgdGhleSBhcmUganVzdCByb3VuZGVkIHRvIDAKCltAbDY7IEBrbml0cl0KCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsZW5ndGgod2hpY2gob3V0cHV0X2hpdHNfb3JnJFAuVmFsdWUgPCAwLjA1KSkKYGBgCltAbDZdCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsZW5ndGgod2hpY2gob3V0cHV0X2hpdHNfb3JnJGFkai5QLlZhbCA8IDAuMDUpKQpgYGAKW0BsNl0KCiMjIENvbXBhcmluZyBib3RoIG1vZGVscwoKVGhlcmUgaXMgYWN0dWFsbHkgbGVzcywgYnV0IGxldCB1cyBsb29rIGF0IGl0IGZyb20gYSBncmFwaAoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmNhcD0nRmlndXJlIDM6IFNob3dpbmcgZWFjaCBnZW5lIG9uIGEgZ3JhcGggd2hlcmUgdGhlIGNvb3JkaW5hdGVzIGlzIHRoZSBjb3JyZWN0ZWQgcC12YWx1ZXMgdXNpbmcgdGhlIHNpbXBsZSBtb2RlbCBhbmQgdGhlIG9yZ2Fub2lkIG1vZGVsIGluIGxpbW1hLCB6b29tZWQgaW4gb24gMCB0byAwLjEuIEhpZ2hsaWdodGluZyBzaWduaWZpY2FudCBnZW5lcyBpbiBib3RoIG1vZGVsIGFzIHJlZCwgaW4gc2ltcGxlIG1vZGVsIG9ubHkgYXMgb3JhbmdlLCBpbiBvcmdhbm9pZCBtb2RlbCBvbmx5IGFzIHJlZCwgYW5kIG5vbi1zaWduaWZpY2FudCBnZW5lcyBhcyBibGFjayd9CnNpbXBsZV9tb2RlbF9wdmFsdWVzIDwtIGRhdGEuZnJhbWUoZW5zZW1ibF9pZCA9CiAgb3V0cHV0X2hpdHMkZW5zZW1ibF9nZW5lX2lkLAogIHNpbXBsZV9wdmFsdWU9b3V0cHV0X2hpdHMkYWRqLlAuVmFsKQoKb3JnX21vZGVsX3B2YWx1ZXMgPC0gIGRhdGEuZnJhbWUoZW5zZW1ibF9pZCA9CiAgb3V0cHV0X2hpdHNfb3JnJGVuc2VtYmxfZ2VuZV9pZCwKICBvcmdhbm9pZF9wdmFsdWUgPSBvdXRwdXRfaGl0c19vcmckYWRqLlAuVmFsKQoKdHdvX21vZGVsc19wdmFsdWVzIDwtIG1lcmdlKHNpbXBsZV9tb2RlbF9wdmFsdWVzLAogIG9yZ19tb2RlbF9wdmFsdWVzLGJ5Lng9MSxieS55PTEpCgp0d29fbW9kZWxzX3B2YWx1ZXMkY29sb3VyIDwtICJibGFjayIKCnR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXJbCiAgdHdvX21vZGVsc19wdmFsdWVzJHNpbXBsZV9wdmFsdWU8MC4wNV0gPC0gIm9yYW5nZSIKCnR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXJbCiAgdHdvX21vZGVsc19wdmFsdWVzJG9yZ2Fub2lkX3B2YWx1ZTwwLjA1XSA8LSAiYmx1ZSIKCnR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXJbCiAgdHdvX21vZGVsc19wdmFsdWVzJHNpbXBsZV9wdmFsdWU8MC4wNSAmCiAgdHdvX21vZGVsc19wdmFsdWVzJG9yZ2Fub2lkX3B2YWx1ZTwwLjA1XSA8LSAicmVkIgoKcGxvdCh0d29fbW9kZWxzX3B2YWx1ZXMkc2ltcGxlX3B2YWx1ZSwKICAgICB0d29fbW9kZWxzX3B2YWx1ZXMkb3JnYW5vaWRfcHZhbHVlLAogICAgIGNvbCA9IHR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXIsCiAgICAgeGxhYiA9ICJTaW1wbGUgbW9kZWwgY29ycmVjdGVkIHAtdmFsdWVzIiwKICAgICB5bGFiID0iT3JnYW5vaWQgbW9kZWwgY29ycmVjdGVkIHAtdmFsdWVzIiwKICAgICBtYWluPSJTaW1wbGUgdnMgT3JnYW5vaWQgTGltbWEiLAogICAgIHhsaW0gPSBjKDAsIDAuMSksCiAgICAgeWxpbSA9IGMoMCwgMC4xKSkKYGBgCkZpZ3VyZSAzOiBTaG93aW5nIGVhY2ggZ2VuZSBvbiBhIGdyYXBoIHdoZXJlIHRoZSBjb29yZGluYXRlcyBpcyB0aGUgY29ycmVjdGVkIHAtdmFsdWVzIHVzaW5nIHRoZSBzaW1wbGUgbW9kZWwgYW5kIHRoZSBvcmdhbm9pZCBtb2RlbCBpbiBsaW1tYSwgem9vbWVkIGluIG9uIDAgdG8gMC4xLiBIaWdobGlnaHRpbmcgc2lnbmlmaWNhbnQgZ2VuZXMgaW4gYm90aCBtb2RlbCBhcyByZWQsIGluIHNpbXBsZSBtb2RlbCBvbmx5IGFzIG9yYW5nZSwgaW4gb3JnYW5vaWQgbW9kZWwgb25seSBhcyByZWQsIGFuZCBub24tc2lnbmlmaWNhbnQgZ2VuZXMgYXMgYmxhY2sKW0BsNl0KClRvIGRldGVybWluZSB3aGljaCBtb2RlbCBpcyBtb3JlIGFwcHJvcHJpYXRlLCB3ZSB0YWtlIGEgbG9vayBhdCBwLXZhbHVlIG9mIHRoZQpnZW5lIG9mIGludGVyZXN0LCBDUlguIAoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmNhcD0nRmlndXJlIDQ6IFNob3dpbmcgZWFjaCBnZW5lIG9uIGEgZ3JhcGggd2hlcmUgdGhlIGNvb3JkaW5hdGVzIGlzIHRoZSBjb3JyZWN0ZWQgcC12YWx1ZXMgdXNpbmcgdGhlIHNpbXBsZSBtb2RlbCBhbmQgdGhlIG9yZ2Fub2lkIG1vZGVsIGluIGxpbW1hLCB6b29tZWQgaW4gb24gMCB0byAwLjEuIEhpZ2hsaWdodGluZyBvbmx5IHRoZSBnZW5lIG9mIGludGVyZXN0LCBDUlggYXMgcmVkJ30KZW5zZW1ibF9vZl9pbnRlcmVzdCA8LSBub3JtYWxpemVkX2NvdW50X2RhdGEkZW5zZW1ibF9nZW5lX2lkWwogIHdoaWNoKG5vcm1hbGl6ZWRfY291bnRfZGF0YSRoZ25jX3N5bWJvbCA9PSAiQ1JYIildCnR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXIgPC0gImdyZXkiCnR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXJbdHdvX21vZGVsc19wdmFsdWVzJGVuc2VtYmxfaWQ9PQogICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5zZW1ibF9vZl9pbnRlcmVzdF0gPC0gInJlZCIKcGxvdCh0d29fbW9kZWxzX3B2YWx1ZXMkc2ltcGxlX3B2YWx1ZSwKICAgICB0d29fbW9kZWxzX3B2YWx1ZXMkb3JnYW5vaWRfcHZhbHVlLAogICAgIGNvbCA9IHR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXIsCiAgICAgeGxhYiA9ICJzaW1wbGUgbW9kZWwgY29ycmVjdGVkIHAtdmFsdWVzIiwKICAgICB5bGFiID0ib3JnYW5vaWQgbW9kZWwgY29ycmVjdGVkICBwLXZhbHVlcyIsCiAgICAgbWFpbj0iU2ltcGxlIHZzIE9yZ2Fub2lkIExpbW1hIiwKICAgICB4bGltID0gYygwLCAwLjEpLAogICAgIHlsaW0gPSBjKDAsIDAuMSkpCnBvaW50cyh0d29fbW9kZWxzX3B2YWx1ZXNbd2hpY2goCiAgdHdvX21vZGVsc19wdmFsdWVzJGVuc2VtYmxfaWQgPT0gZW5zZW1ibF9vZl9pbnRlcmVzdCksMjozXSwKICAgICAgIHBjaD0yMCwgY29sPSJyZWQiLCBjZXg9MS41KQpsZWdlbmQoMCwxLGxlZ2VuZD1jKCJDUlgiLCJyZXN0IiksCiAgICAgICBmaWxsPWMoInJlZCIsImdyZXkiKSxjZXggPSAwLjcpCmBgYApGaWd1cmUgNDogU2hvd2luZyBlYWNoIGdlbmUgb24gYSBncmFwaCB3aGVyZSB0aGUgY29vcmRpbmF0ZXMgaXMgdGhlIGNvcnJlY3RlZCBwLXZhbHVlcyB1c2luZyB0aGUgc2ltcGxlIG1vZGVsIGFuZCB0aGUgb3JnYW5vaWQgbW9kZWwgaW4gbGltbWEsIHpvb21lZCBpbiBvbiAwIHRvIDAuMS4gSGlnaGxpZ2h0aW5nIG9ubHkgdGhlIGdlbmUgb2YgaW50ZXJlc3QsIENSWCBhcyByZWQKW0BsNl0KClRoZSBzaW1wbGUgbW9kZWwgaXMgYmV0dGVyLCBJIGJlbGlldmUgdGhhdCBpcyB0aGUgYmVjYXVzZSB0aGVyZSBhcmUgbWluaW1hbCAKZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSByZXRpbmFsIG9yZ2Fub2lkcywgc2luY2UgdGhleSBhcmUgZGVyaXZlZCBmcm9tIGlQU0MgCnRoYXQgaXMgZnJvbSB0aGUgc2FtZSBwYXRpZW50LiAKCgojIyBIZWF0bWFwIGZyb20gbGltbWEgCgpMZXQgdXMgbG9vayBhdCB0aGUgaGVhdG1hcCBvZiBvbmx5IHRoZSB0b3AgaGl0cyAoYnkgY29ycmVjdGVkIHB2YWx1ZSksCmluIHRoZSBzaW1wbGUgbW9kZWwKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5jYXA9J0ZpZ3VyZSA1OiBIZWF0bWFwIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBvZiB0aGUgZmlyc3QgMTAwMCBnZW5lcywgb3JkZXJlZCBieSBlbnNlbWJsIGlkLCBhZnRlciByb3cgbm9ybWFsaXphdGlvbiBmb3Igc2lnbmlmaWNhbnQgZ2VuZXMgYnkgY29ycmVjdGVkIHAtdmFsdWUgdXNpbmcgdGhlIHNpbXBsZSBtb2RlbCBpbiBsaW1tYS4gT25seSB0aGUgZmlyc3QgMTAwMCBnZW5lcyBhcmUgc2hvd24gZHVlIHRvIHJlbmRlcmluZyByZXNvdXJjZSBpc3N1ZXMnfQp0b3BfaGl0cyA8LSBvdXRwdXRfaGl0cyRlbnNlbWJsX2dlbmVfaWRbCiAgb3V0cHV0X2hpdHMkYWRqLlAuVmFsPDAuMDVdCgpoZWF0bWFwX21hdHJpeF90b3BoaXRzIDwtIHQoCiAgc2NhbGUodChoZWF0bWFwX21hdHJpeFsKd2hpY2gocm93bmFtZXMoaGVhdG1hcF9tYXRyaXgpICVpbiUgdG9wX2hpdHMpLF0pKSkKCgppZihtaW4oaGVhdG1hcF9tYXRyaXhfdG9waGl0cykgPT0gMCl7CmhlYXRtYXBfY29sID0gY29sb3JSYW1wMihjKCAwLCBtYXgoaGVhdG1hcF9hdHJpeF90b3BoaXRzKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYyggIndoaXRlIiwgInJlZCIpKQp9IGVsc2UgewpoZWF0bWFwX2NvbCA9IGNvbG9yUmFtcDIoYyhtaW4oaGVhdG1hcF9tYXRyaXhfdG9waGl0cyksIDAsCiAgICAgIG1heChoZWF0bWFwX21hdHJpeF90b3BoaXRzKSksIGMoImJsdWUiLCAid2hpdGUiLCAicmVkIikpCiAgfQoKSGVhdG1hcChhcy5tYXRyaXgoaGVhdG1hcF9tYXRyaXhfdG9waGl0c1sxOjEwMDAsXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfY29sdW1ucyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X3Jvd19kZW5kID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfY29sdW1uX2RlbmQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sPWhlYXRtYXBfY29sLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19jb2x1bW5fbmFtZXMgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19yb3dfbmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfaGVhdG1hcF9sZWdlbmQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKYGBgCkZpZ3VyZSA1OiBIZWF0bWFwIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBvZiB0aGUgZmlyc3QgMTAwMCBnZW5lcywgb3JkZXJlZCBieSBlbnNlbWJsIGlkLCBhZnRlciByb3cgbm9ybWFsaXphdGlvbiBmb3Igc2lnbmlmaWNhbnQgZ2VuZXMgYnkgY29ycmVjdGVkIHAtdmFsdWUgdXNpbmcgdGhlIHNpbXBsZSBtb2RlbCBpbiBsaW1tYS4gT25seSB0aGUgZmlyc3QgMTAwMCBnZW5lcyBhcmUgc2hvd24gZHVlIHRvIHJlbmRlcmluZyByZXNvdXJjZSBpc3N1ZXMKW0BsNl0KCgpMZXQgdXMgbm90IGNsdXN0ZXIgdGhlIGdlbmVzIGJ1dCBncm91cCBjb250cm9scyBhbmQgZXhwZXJpbWVudHMgdG9nZXRoZXIKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5jYXA9J0ZpZ3VyZSA2OiBIZWF0bWFwIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBvZiB0aGUgZmlyc3QgMTAwMCBnZW5lcywgb3JkZXJlZCBieSBlbnNlbWJsIGlkLCBhZnRlciByb3cgbm9ybWFsaXphdGlvbiBmb3Igc2lnbmlmaWNhbnQgZ2VuZXMgYmFzZWQgb24gY29ycmVjdGVkIHAtdmFsdWUgdXNpbmcgdGhlIHNpbXBsZSBtb2RlbCBpbiBsaW1tYSwgd2l0aG91dCBjbHVzdGVyaW5nLCBhbmQgZ3JvdXAgY29udHJvbCBhbmQgZXhwZXJpbWVudCB0b2dldGhlciwgcmVzcGVjdGl2ZWx5LiBPbmx5IHRoZSBmaXJzdCAxMDAwIGdlbmVzIGFyZSBzaG93biBkdWUgdG8gcmVuZGVyaW5nIHJlc291cmNlIGlzc3Vlcyd9CnRvcF9oaXRzIDwtIG91dHB1dF9oaXRzJGVuc2VtYmxfZ2VuZV9pZFsKICBvdXRwdXRfaGl0cyRhZGouUC5WYWw8MC4wNV0KCmhlYXRtYXBfbWF0cml4X3RvcGhpdHMgPC0gdCgKICBzY2FsZSh0KGhlYXRtYXBfbWF0cml4Wwp3aGljaChyb3duYW1lcyhoZWF0bWFwX21hdHJpeCkgJWluJSB0b3BfaGl0cyksXSkpKQoKaGVhdG1hcF9tYXRyaXhfdG9waGl0cyA8LSBoZWF0bWFwX21hdHJpeF90b3BoaXRzWywKYygKICBncmVwKGNvbG5hbWVzKGhlYXRtYXBfbWF0cml4X3RvcGhpdHMpLCBwYXR0ZXJuID0gIlxcQ1JYTENBIiksCiAgZ3JlcChjb2xuYW1lcyhoZWF0bWFwX21hdHJpeF90b3BoaXRzKSwgcGF0dGVybiA9ICJcXENvbnRyb2wiKQogKQpdCgppZihtaW4oaGVhdG1hcF9tYXRyaXhfdG9waGl0cykgPT0gMCl7CmhlYXRtYXBfY29sID0gY29sb3JSYW1wMihjKCAwLCBtYXgoaGVhdG1hcF9tYXRyaXhfdG9waGl0cykpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoICJ3aGl0ZSIsICJyZWQiKSkKfSBlbHNlIHsKaGVhdG1hcF9jb2wgPSBjb2xvclJhbXAyKGMobWluKGhlYXRtYXBfbWF0cml4X3RvcGhpdHMpLCAwLAogICAgICAgICAgICAgICAgICAgIG1heChoZWF0bWFwX21hdHJpeF90b3BoaXRzKSksCiAgICAgICAgICAgICAgICAgICAgYygiYmx1ZSIsICJ3aGl0ZSIsICJyZWQiKSkKfQoKSGVhdG1hcChhcy5tYXRyaXgoaGVhdG1hcF9tYXRyaXhfdG9waGl0c1sxOjEwMDAsXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfY29sdW1ucyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19yb3dfZGVuZCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2NvbHVtbl9kZW5kID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbD1oZWF0bWFwX2NvbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfY29sdW1uX25hbWVzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfcm93X25hbWVzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2hlYXRtYXBfbGVnZW5kID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKYGBgCkZpZ3VyZSA2OiBIZWF0bWFwIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBvZiB0aGUgZmlyc3QgMTAwMCBnZW5lcywgb3JkZXJlZCBieSBlbnNlbWJsIGlkLCBhZnRlciByb3cgbm9ybWFsaXphdGlvbiBmb3Igc2lnbmlmaWNhbnQgZ2VuZXMgYmFzZWQgb24gY29ycmVjdGVkIHAtdmFsdWUgdXNpbmcgdGhlIHNpbXBsZSBtb2RlbCBpbiBsaW1tYSwgd2l0aG91dCBjbHVzdGVyaW5nLCBhbmQgZ3JvdXAgY29udHJvbCBhbmQgZXhwZXJpbWVudCB0b2dldGhlciwgcmVzcGVjdGl2ZWx5LiBPbmx5IHRoZSBmaXJzdCAxMDAwIGdlbmVzIGFyZSBzaG93biBkdWUgdG8gcmVuZGVyaW5nIHJlc291cmNlIGlzc3VlcwpbQGw2XQoKV2UgY2FuIGNsZWFybHkgdGVsbCB0aGF0IHRoZSB0ZXN0IGNvbmRpdGlvbiBkbyBub3QgbG9vayBsaWtlIGVhY2ggb3RoZXIuClRoZSBjb250cm9scyBhbHNvIGRvIG5vdCBsb29rIGxpa2UgZWFjaCBvdGhlci4KCgpUaGVuIGFnYWluIGxvb2sgYXQgdG9wIGhpdHMgd2l0aCBsZXNzIHRoYW4gMC4wMSBwdmFsdWUKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmNhcD0nRmlndXJlIDc6IEhlYXRtYXAgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIG9mIHRoZSBmaXJzdCAxMDAwIGdlbmVzLCBvcmRlcmVkIGJ5IGVuc2VtYmwgaWQsIGFmdGVyIHJvdyBub3JtYWxpemF0aW9uIGZvciBoaXRzIHdpdGggY29ycmVjdGVkIHAtdmFsdWUgc21hbGxlciB0aGFuIDAuMDEgdXNpbmcgdGhlIHNpbXBsZSBtb2RlbCBpbiBsaW1tYSwgd2l0aG91dCBjbHVzdGVyaW5nLCBhbmQgZ3JvdXAgY29udHJvbCBhbmQgZXhwZXJpbWVudCB0b2dldGhlciwgcmVzcGVjdGl2ZWx5LiBPbmx5IHRoZSBmaXJzdCAxMDAwIGdlbmVzIGFyZSBzaG93biBkdWUgdG8gcmVuZGVyaW5nIHJlc291cmNlIGlzc3Vlcyd9CnRvcF9oaXRzIDwtIG91dHB1dF9oaXRzJGVuc2VtYmxfZ2VuZV9pZFsKICBvdXRwdXRfaGl0cyRhZGouUC5WYWwgPCAwLjAxXQoKaGVhdG1hcF9tYXRyaXhfdG9waGl0cyA8LSB0KApzY2FsZSh0KGhlYXRtYXBfbWF0cml4W3doaWNoKHJvd25hbWVzKGhlYXRtYXBfbWF0cml4KSAlaW4lIHRvcF9oaXRzKSxdKSkpCgpoZWF0bWFwX21hdHJpeF90b3BoaXRzIDwtIGhlYXRtYXBfbWF0cml4X3RvcGhpdHNbLAogICAgICAgYyhncmVwKGNvbG5hbWVzKGhlYXRtYXBfbWF0cml4X3RvcGhpdHMpLHBhdHRlcm4gPSAiXFxDUlhMQ0EiKSwKICAgICAgICAgZ3JlcChjb2xuYW1lcyhoZWF0bWFwX21hdHJpeF90b3BoaXRzKSxwYXR0ZXJuID0gIlxcQ29udHJvbCIpKV0KCm5yb3coaGVhdG1hcF9tYXRyaXhfdG9waGl0cykKCmlmKG1pbihoZWF0bWFwX21hdHJpeF90b3BoaXRzKSA9PSAwKXsKaGVhdG1hcF9jb2wgPSBjb2xvclJhbXAyKGMoIDAsIG1heChoZWF0bWFwX21hdHJpeF90b3BoaXRzKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYyggIndoaXRlIiwgInJlZCIpKQp9IGVsc2UgewpoZWF0bWFwX2NvbCA9IGNvbG9yUmFtcDIoYyhtaW4oaGVhdG1hcF9tYXRyaXhfdG9waGl0cyksIDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heChoZWF0bWFwX21hdHJpeF90b3BoaXRzKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiYmx1ZSIsICJ3aGl0ZSIsICJyZWQiKSkKfQpIZWF0bWFwKGFzLm1hdHJpeChoZWF0bWFwX21hdHJpeF90b3BoaXRzWzE6MTAwMCxdKSwKICAgICAgICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IFRSVUUsICBzaG93X3Jvd19kZW5kID0gVFJVRSwKICAgICAgICAgICAgICAgIGNsdXN0ZXJfY29sdW1ucyA9IEZBTFNFLHNob3dfY29sdW1uX2RlbmQgPSBGQUxTRSwKICAgICAgICAgICAgICAgIGNvbD1oZWF0bWFwX2NvbCxzaG93X2NvbHVtbl9uYW1lcyA9IFRSVUUsCiAgICAgICAgICAgICAgICBzaG93X3Jvd19uYW1lcyA9IEZBTFNFLHNob3dfaGVhdG1hcF9sZWdlbmQgPSBUUlVFKQpgYGAKRmlndXJlIDc6IEhlYXRtYXAgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIG9mIHRoZSBmaXJzdCAxMDAwIGdlbmVzLCBvcmRlcmVkIGJ5IGVuc2VtYmwgaWQsIGFmdGVyIHJvdyBub3JtYWxpemF0aW9uIGZvciBoaXRzIHdpdGggY29ycmVjdGVkIHAtdmFsdWUgc21hbGxlciB0aGFuIDAuMDEgdXNpbmcgdGhlIHNpbXBsZSBtb2RlbCBpbiBsaW1tYSwgd2l0aG91dCBjbHVzdGVyaW5nLCBhbmQgZ3JvdXAgY29udHJvbCBhbmQgZXhwZXJpbWVudCB0b2dldGhlciwgcmVzcGVjdGl2ZWx5LiBPbmx5IHRoZSBmaXJzdCAxMDAwIGdlbmVzIGFyZSBzaG93biBkdWUgdG8gcmVuZGVyaW5nIHJlc291cmNlIGlzc3VlcwpbQGw2OyBAaGVhdG1hcDsgQGNvbG9yXQoKCiMgSGVhdG1hcCB3aXRoIHRvcGhpdHMgZnJvbSBlZGdlUgoKIyMgU2ltcGxlIE1vZGVsCgpUaGUgc2ltcGxlIG1vZGVsIGlzIHdoZXJlIHRoZSBtb2RlbCBhY2NvdW50cyBmb3IgdGhlIGNvbmRpdGlvbiAoY29udHJvbCB2cyAKZXhwZXJpbWVudCksIGFuZCB0aGUgZGF5IHRoYXQgdGhlIGRhdGEgd2FzIGNvbGxlY3RlZCBvbiwgYnV0IG5vdCBhY2NvdW50IApmb3Igd2hpY2ggb3JnYW5vaWRzIGlzIHRoZSBkYXRhIGNvbGxlY3RlZCBmcm9tLiAKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmZpbHRlcmVkX2RhdGFfbWF0cml4IDwtIGFzLm1hdHJpeChDUlhfRXhwZXJpbWVudFJhd0NvdW50RmlsdGVyZWRbLDM6MjhdKQpyb3duYW1lcyhmaWx0ZXJlZF9kYXRhX21hdHJpeCkgPC0gQ1JYX0V4cGVyaW1lbnRSYXdDb3VudEZpbHRlcmVkJGVucy5pZApkIDwtIGVkZ2VSOjpER0VMaXN0KGNvdW50cz1maWx0ZXJlZF9kYXRhX21hdHJpeCwgZ3JvdXA9c2FtcGxlcyRjb25kaXRpb24pCmQgPC0gZWRnZVI6OmVzdGltYXRlRGlzcChkLCBtb2RlbF9kZXNpZ24pCmZpdCA8LSBlZGdlUjo6Z2xtUUxGaXQoZCwgbW9kZWxfZGVzaWduKQoKYGBgCltAbDY7IEBlZGdlcl0KCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5jYXA9J1RhYmxlIDY6IFNob3cgdGhlIG91dHB1dCBvZiBnbG1RTEZpdCBhZnRlciBmaXR0aW5nIHRoZSBkYXRhIHRvIHRoZSBzaW1wbGUgbW9kZWwsIGNhbGN1bGF0aW5nIHRoZSBwLXZhbHVlLCBhbmQgY29ycmVjdGluZyB0aGUgcC12YWx1ZS4gVGhpcyBpcyBvbmx5IHNob3dpbmcgdGhlIGZpcnN0IDEwLCBvcmRlcmVkIGJ5IHAtdmFsdWUuIE5vdGUgdGhhdCB0aGUgcCB2YWx1ZSBhbmQgRkRSIGFyZSBub3QgYWN0dWFsbHkgMCwgdGhleSBhcmUganVzdCByb3VuZGVkIHRvIDAnfQpxbGYucG9zX3ZzX25lZyA8LSBlZGdlUjo6Z2xtUUxGVGVzdChmaXQsIGNvZWY9J3NhbXBsZXMkY29uZGl0aW9uQ1JYTENBJykKa25pdHI6OmthYmxlKGVkZ2VSOjp0b3BUYWdzKHFsZi5wb3NfdnNfbmVnKSwgdHlwZT0iaHRtbCIscm93Lm5hbWVzID0gRkFMU0UpCmBgYAoKCltAbDY7IEBlZGdlcjsgQGtuaXRyXQoKRXZlbiB0aG91Z2ggdGhpcyBzYXkgRkRSLCBidXQgc2luY2Ugd2UgYXJlIHVzaW5nIHRoZSBzYW1lIG11bHRpcGxlIGh5cG90aGVzaXMgCnRlc3RpbmcgbWV0aG9kIGZvciBib3RoIGVkZ2VSIGFuZCBsaW1tYSwgdGhleSBhcmUgdGhlIHNhbWUgdHlwZSBvZiBjb3JyZWN0ZWQKdmFsdWVzLiBUaGUgZmlyc3Qgb25lIGlzIGdlbmUgYmVmb3JlIGNvcnJlY3Rpb24sIGFuZCBzZWNvbmQgb25lIGlzIGFmdGVyIGNvcnJlY3Rpb24KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcWxmX291dHB1dF9oaXRzIDwtIGVkZ2VSOjp0b3BUYWdzKHFsZi5wb3NfdnNfbmVnLHNvcnQuYnkgPSAiUFZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbiA9IG5yb3cobm9ybWFsaXplZF9jb3VudF9kYXRhKSkKbGVuZ3RoKHdoaWNoKHFsZl9vdXRwdXRfaGl0cyR0YWJsZSRQVmFsdWUgPCAwLjA1KSkKbGVuZ3RoKHdoaWNoKHFsZl9vdXRwdXRfaGl0cyR0YWJsZSRGRFIgPCAwLjA1KSkKYGBgCltAbDY7IEBlZGdlcl0KCiMjIE9yZ2Fub2lkIG1vZGVsCgpUaGUgb3JnYW5vaWQgTW9kZWwgaXMgd2hlcmUgdGhlIG1vZGVsIGFjY291bnRzIGZvciB0aGUgY29uZGl0aW9uIChjb250cm9sIHZzIApleHBlcmltZW50KSwgYW5kIHRoZSBkYXkgdGhhdCB0aGUgZGF0YSB3YXMgY29sbGVjdGVkIG9uLCBhbmQgCmZvciB3aGljaCBvcmdhbm9pZHMgaXMgdGhlIGRhdGEgY29sbGVjdGVkIGZyb20uIAoKYGBge3Igd2FybmluZz1GQUxTRX0KZmlsdGVyZWRfZGF0YV9tYXRyaXggPC0gYXMubWF0cml4KENSWF9FeHBlcmltZW50UmF3Q291bnRGaWx0ZXJlZFssMzoyOF0pCnJvd25hbWVzKGZpbHRlcmVkX2RhdGFfbWF0cml4KSA8LSBDUlhfRXhwZXJpbWVudFJhd0NvdW50RmlsdGVyZWQkZW5zLmlkCmQgPC0gZWRnZVI6OkRHRUxpc3QoY291bnRzPWZpbHRlcmVkX2RhdGFfbWF0cml4LCBncm91cD1zYW1wbGVzJGNvbmRpdGlvbikKZF9vcmcgPC0gZWRnZVI6OmVzdGltYXRlRGlzcChkLCBtb2RlbF9kZXNpZ25fb3JnKQpmaXRfb3JnIDwtIGVkZ2VSOjpnbG1RTEZpdChkX29yZywgbW9kZWxfZGVzaWduX29yZykKCmBgYApbQGw2OyBAZWRnZXJdCgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmNhcD0nVGFibGUgNzogU2hvdyB0aGUgb3V0cHV0IG9mIGdsbVFMRml0IGFmdGVyIGZpdHRpbmcgdGhlIGRhdGEgdG8gdGhlIG9yZ2Fub2lkIG1vZGVsLCBjYWxjdWxhdGluZyB0aGUgcC12YWx1ZSwgYW5kIGNvcnJlY3RpbmcgdGhlIHAtdmFsdWUuIFRoaXMgaXMgb25seSBzaG93aW5nIHRoZSBmaXJzdCAxMCwgb3JkZXJlZCBieSBwLXZhbHVlLiBOb3RlIHRoYXQgdGhlIHAgdmFsdWUgYW5kIEZEUiBhcmUgbm90IGFjdHVhbGx5IDAsIHRoZXkgYXJlIGp1c3Qgcm91bmRlZCB0byAwJ30KcWxmLnBvc192c19uZWcgPC0gZWRnZVI6OmdsbVFMRlRlc3QoZml0X29yZywgY29lZj0nc2FtcGxlcyRjb25kaXRpb25DUlhMQ0EnKQprbml0cjo6a2FibGUoZWRnZVI6OnRvcFRhZ3MocWxmLnBvc192c19uZWcpLCB0eXBlPSJodG1sIixyb3cubmFtZXMgPSBGQUxTRSkKYGBgCgoKW0BsNjsgQGtuaXRyOyBAZWRnZXJdCgpUaGUgZmlyc3Qgb25lIGlzIGdlbmUgYmVmb3JlIGNvcnJlY3Rpb24sIGFuZCBzZWNvbmQgb25lIGlzIGFmdGVyIGNvcnJlY3Rpb24KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcWxmX291dHB1dF9oaXRzX29yZyA8LSBlZGdlUjo6dG9wVGFncyhxbGYucG9zX3ZzX25lZyxzb3J0LmJ5ID0gIlBWYWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG4gPSBucm93KG5vcm1hbGl6ZWRfY291bnRfZGF0YSkpCmxlbmd0aCh3aGljaChxbGZfb3V0cHV0X2hpdHNfb3JnJHRhYmxlJFBWYWx1ZSA8IDAuMDUpKQpsZW5ndGgod2hpY2gocWxmX291dHB1dF9oaXRzX29yZyR0YWJsZSRGRFIgPCAwLjA1KSkKYGBgCltAbDY7IEBlZGdlcl0KCiMjIENvbXBhcmUgMiBtb2RlbCBmcm9tIGVkZ2VSCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSdGaWd1cmUgODogU2hvd2luZyBlYWNoIGdlbmUgb24gYSBncmFwaCB3aGVyZSB0aGUgY29vcmRpbmF0ZXMgaXMgdGhlIGNvcnJlY3RlZCBwLXZhbHVlcyB1c2luZyB0aGUgc2ltcGxlIG1vZGVsIGFuZCB0aGUgb3JnYW5vaWQgbW9kZWwgaW4gZ2xtUUxGaXQsIHpvb21lZCBpbiBvbiAwLTAuMS4gSGlnaGxpZ2h0aW5nIHNpZ25pZmljYW50IGdlbmVzIGluIGJvdGggbW9kZWwgYXMgcmVkLCBpbiBzaW1wbGUgbW9kZWwgb25seSBhcyBvcmFuZ2UsIGluIG9yZ2Fub2lkIG1vZGVsIG9ubHkgYXMgcmVkLCBhbmQgbm9uLXNpZ25pZmljYW50IGdlbmVzIGFzIGJsYWNrJ30Kc2ltcGxlX21vZGVsX3B2YWx1ZXNfcWxmIDwtIGRhdGEuZnJhbWUoZW5zZW1ibF9pZCA9CiAgcm93bmFtZXMocWxmX291dHB1dF9oaXRzJHRhYmxlKSwKICBzaW1wbGVfcHZhbHVlID0gcWxmX291dHB1dF9oaXRzJHRhYmxlJEZEUikKCm9yZ19tb2RlbF9wdmFsdWVzX3FsZiA8LSAgZGF0YS5mcmFtZShlbnNlbWJsX2lkID0KICByb3duYW1lcyhxbGZfb3V0cHV0X2hpdHNfb3JnJHRhYmxlKSwKICBvcmdhbm9pZF9wdmFsdWUgPSBxbGZfb3V0cHV0X2hpdHNfb3JnJHRhYmxlJEZEUikKCnR3b19tb2RlbHNfcHZhbHVlcyA8LSBtZXJnZShzaW1wbGVfbW9kZWxfcHZhbHVlc19xbGYsCiAgb3JnX21vZGVsX3B2YWx1ZXNfcWxmLCBieS54PTEsIGJ5Lnk9MSkKCnR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXIgPC0gImJsYWNrIgoKdHdvX21vZGVsc19wdmFsdWVzJGNvbG91clsKICB0d29fbW9kZWxzX3B2YWx1ZXMkc2ltcGxlX3B2YWx1ZTwwLjA1XSA8LSAib3JhbmdlIgoKdHdvX21vZGVsc19wdmFsdWVzJGNvbG91clsKICB0d29fbW9kZWxzX3B2YWx1ZXMkb3JnYW5vaWRfcHZhbHVlPDAuMDVdIDwtICJibHVlIgoKdHdvX21vZGVsc19wdmFsdWVzJGNvbG91clsKICB0d29fbW9kZWxzX3B2YWx1ZXMkc2ltcGxlX3B2YWx1ZTwwLjA1ICYKICB0d29fbW9kZWxzX3B2YWx1ZXMkb3JnYW5vaWRfcHZhbHVlPDAuMDVdIDwtICJyZWQiCgpwbG90KHR3b19tb2RlbHNfcHZhbHVlcyRzaW1wbGVfcHZhbHVlLAogICAgIHR3b19tb2RlbHNfcHZhbHVlcyRvcmdhbm9pZF9wdmFsdWUsCiAgICAgY29sID0gdHdvX21vZGVsc19wdmFsdWVzJGNvbG91ciwKICAgICB4bGFiID0gIlNpbXBsZSBtb2RlbCBjb3JyZWN0ZWQgcC12YWx1ZXMiLAogICAgIHlsYWIgPSJPcmdhbm9pZCBtb2RlbCBjb3JyZWN0ZWQgcC12YWx1ZXMiLAogICAgIG1haW49IlNpbXBsZSB2cyBPcmdhbm9pZCBlZGdlUiIsCiAgICAgeGxpbT1jKDAsIDAuMSksCiAgICAgeWxpbT1jKDAsIDAuMSkpCmBgYApGaWd1cmUgODogU2hvd2luZyBlYWNoIGdlbmUgb24gYSBncmFwaCB3aGVyZSB0aGUgY29vcmRpbmF0ZXMgaXMgdGhlIGNvcnJlY3RlZCBwLXZhbHVlcyB1c2luZyB0aGUgc2ltcGxlIG1vZGVsIGFuZCB0aGUgb3JnYW5vaWQgbW9kZWwgaW4gZ2xtUUxGaXQsIHpvb21lZCBpbiBvbiAwLTAuMS4gSGlnaGxpZ2h0aW5nIHNpZ25pZmljYW50IGdlbmVzIGluIGJvdGggbW9kZWwgYXMgcmVkLCBpbiBzaW1wbGUgbW9kZWwgb25seSBhcyBvcmFuZ2UsIGluIG9yZ2Fub2lkIG1vZGVsIG9ubHkgYXMgcmVkLCBhbmQgbm9uLXNpZ25pZmljYW50IGdlbmVzIGFzIGJsYWNrCltAbDZdCgpUaGUgbWFqb3JpdHkgb2YgZ2VuZXMgdGhhdCBoYXZlIGxlc3MgdGhhbiAwLjA1IHAtdmFsdWUgaW4gZWl0aGVyIG1vZGVsLCBoYXMgbGVzcyB0aGFuIDAuMDUgCnAtdmFsdWUgaW4gYm90aCBtb2RlbC4KClRvIGRldGVybWluZSB3aGljaCBtb2RlbCBpcyBtb3JlIGFwcHJvcHJpYXRlLCB3ZSB0YWtlIGEgbG9vayBhdCBwPXZhbHVlIG9mIHRoZQpnZW5lIG9mIGludGVyZXN0LCBDUlguIAoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmNhcD0nRmlndXJlIDk6IFNob3dpbmcgZWFjaCBnZW5lIG9uIGEgZ3JhcGggd2hlcmUgdGhlIGNvb3JkaW5hdGVzIGlzIHRoZSBjb3JyZWN0ZWQgcC12YWx1ZXMgdXNpbmcgdGhlIHNpbXBsZSBtb2RlbCBhbmQgdGhlIG9yZ2Fub2lkIG1vZGVsIGluIGdsbVFMRml0LCB6b29tZWQgaW4gb24gMC0wLjAxLiBIaWdobGlnaHRpbmcgb25seSB0aGUgZ2VuZSBvZiBpbnRlcmVzdCwgQ1JYJ30KZW5zZW1ibF9vZl9pbnRlcmVzdCA8LSBub3JtYWxpemVkX2NvdW50X2RhdGEkZW5zZW1ibF9nZW5lX2lkWwogIHdoaWNoKG5vcm1hbGl6ZWRfY291bnRfZGF0YSRoZ25jX3N5bWJvbCA9PSAiQ1JYIildCnR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXIgPC0gImdyZXkiCnR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXJbdHdvX21vZGVsc19wdmFsdWVzJGVuc2VtYmxfaWQ9PQogICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5zZW1ibF9vZl9pbnRlcmVzdF0gPC0gInJlZCIKcGxvdCh0d29fbW9kZWxzX3B2YWx1ZXMkc2ltcGxlX3B2YWx1ZSwKICAgICB0d29fbW9kZWxzX3B2YWx1ZXMkb3JnYW5vaWRfcHZhbHVlLAogICAgIGNvbCA9IHR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXIsCiAgICAgeGxhYiA9ICJzaW1wbGUgbW9kZWwgY29ycmVjdGVkIHAtdmFsdWVzIiwKICAgICB5bGFiID0ib3JnYW5vaWQgbW9kZWwgY29ycmVjdGVkIHAtdmFsdWVzIiwKICAgICBtYWluPSJTaW1wbGUgdnMgT3JnYW5vaWQgTGltbWEiLAogICAgIHhsaW0gPSBjKDAsIDAuMDEpLAogICAgIHlsaW0gPSBjKDAsIDAuMDEpKQpwb2ludHModHdvX21vZGVsc19wdmFsdWVzW3doaWNoKAogIHR3b19tb2RlbHNfcHZhbHVlcyRlbnNlbWJsX2lkID09IGVuc2VtYmxfb2ZfaW50ZXJlc3QpLDI6M10sCiAgICAgICBwY2g9MjAsIGNvbD0icmVkIiwgY2V4PTEuNSkKbGVnZW5kKDAsMSxsZWdlbmQ9YygiQ1JYIiwicmVzdCIpLAogICAgICAgZmlsbD1jKCJyZWQiLCJncmV5IiksY2V4ID0gMC43KQpgYGAKRmlndXJlIDk6IFNob3dpbmcgZWFjaCBnZW5lIG9uIGEgZ3JhcGggd2hlcmUgdGhlIGNvb3JkaW5hdGVzIGlzIHRoZSBjb3JyZWN0ZWQgcC12YWx1ZXMgdXNpbmcgdGhlIHNpbXBsZSBtb2RlbCBhbmQgdGhlIG9yZ2Fub2lkIG1vZGVsIGluIGdsbVFMRml0LCB6b29tZWQgaW4gb24gMC0wLjAxLiBIaWdobGlnaHRpbmcgb25seSB0aGUgZ2VuZSBvZiBpbnRlcmVzdCwgQ1JYCltAbDZdCgpUaGUgc2ltcGxlIG1vZGVsIGlzIGJldHRlciwgSSBiZWxpZXZlIHRoaXMgaXMgc3RpbGwgZHVlIHRvIHRoZSBzYW1lIHJlYXNvbiB0aGF0LAp0aGVyZSBhcmUgbWluaW1hbCBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIHJldGluYWwgb3JnYW5vaWRzLCBzaW5jZSB0aGV5IGFyZSAKZGVyaXZlZCBmcm9tIGlQU0MgdGhhdCBpcyBmcm9tIHRoZSBzYW1lIHBhdGllbnQuIAoKCiMjIENvbXBhcmUgbGltbWEgd2l0aCBxdWFzaS1saWtlbGlob29kCgoKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5jYXA9J0ZpZ3VyZSAxMDogU2hvd2luZyBlYWNoIGdlbmUgb24gYSBncmFwaCB3aGVyZSB0aGUgY29vcmRpbmF0ZXMgaXMgdGhlIGNvcnJlY3RlZCBwLXZhbHVlcyB1c2luZyB0aGUgc2ltcGxlIG1vZGVsIGluIGdsbVFMRml0IGFuZCBpbiBsaW1tYS4gSGlnaGxpZ2h0aW5nIHNpZ25pZmljYW50IGdlbmVzIGluIGJvdGggbW9kZWwgYXMgcmVkLCBpbiBzaW1wbGUgbW9kZWwgb25seSBhcyBvcmFuZ2UsIGluIG9yZ2Fub2lkIG1vZGVsIG9ubHkgYXMgcmVkLCBhbmQgbm9uLXNpZ25pZmljYW50IGdlbmVzIGFzIGJsYWNrJ30KcWxmX3NpbXBsZV9tb2RlbF9wdmFsdWVzIDwtIGRhdGEuZnJhbWUoCiAgICAgICAgICBlbnNlbWJsX2lkID0gcm93bmFtZXMocWxmX291dHB1dF9oaXRzJHRhYmxlKSwKICAgICAgICAgIHFsZl9zaW1wbGVfcHZhbHVlID0gcWxmX291dHB1dF9oaXRzJHRhYmxlJEZEUikKCmxpbW1hX3NpbXBsZV9tb2RlbF9wdmFsdWVzIDwtICBkYXRhLmZyYW1lKAogICAgICAgICAgZW5zZW1ibF9pZCA9IG91dHB1dF9oaXRzJGVuc2VtYmxfZ2VuZV9pZCwKICAgICAgICAgIGxpbW1hX3NpbXBsZV9wdmFsdWUgPSBvdXRwdXRfaGl0cyRhZGouUC5WYWwpCgp0d29fbW9kZWxzX3B2YWx1ZXMgPC0gbWVyZ2UocWxmX3NpbXBsZV9tb2RlbF9wdmFsdWVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbGltbWFfc2ltcGxlX21vZGVsX3B2YWx1ZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBieS54PTEsYnkueT0xKQoKdHdvX21vZGVsc19wdmFsdWVzJGNvbG91ciA8LSAiYmxhY2siCgp0d29fbW9kZWxzX3B2YWx1ZXMkY29sb3VyW3R3b19tb2RlbHNfcHZhbHVlcyRxbGZfc2ltcGxlX3B2YWx1ZQogICAgICAgICAgICAgICAgICAgICAgICAgIDwwLjA1XSA8LSAib3JhbmdlIgoKdHdvX21vZGVsc19wdmFsdWVzJGNvbG91clt0d29fbW9kZWxzX3B2YWx1ZXMkbGltbWFfc2ltcGxlX3B2YWx1ZQogICAgICAgICAgICAgICAgICAgICAgICAgIDwwLjA1XSA8LSAiYmx1ZSIKCnR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXJbdHdvX21vZGVsc19wdmFsdWVzJHFsZl9zaW1wbGVfcHZhbHVlCiAgICAgICAgICAgICAgICAgICAgICAgICAgPDAuMDUgJgp0d29fbW9kZWxzX3B2YWx1ZXMkbGltbWFfc2ltcGxlX3B2YWx1ZTwwLjA1XSA8LSAicmVkIgoKcGxvdCh0d29fbW9kZWxzX3B2YWx1ZXMkcWxmX3NpbXBsZV9wdmFsdWUsCiAgICAgdHdvX21vZGVsc19wdmFsdWVzJGxpbW1hX3NpbXBsZV9wdmFsdWUsCiAgICAgY29sID0gdHdvX21vZGVsc19wdmFsdWVzJGNvbG91ciwKICAgICB4bGFiID0gIlFMRiBzaW1wbGUgbW9kZWwgYWRqdXN0ZWQgcC12YWx1ZXMiLAogICAgIHlsYWIgPSJMaW1tYSBzaW1wbGUgbW9kZWwgYWRqdXN0ZWQgcC12YWx1ZXMiLAogICAgIG1haW49IlFMRiB2cyBMaW1tYSIsCiAgICAgeGxpbSA9IGMoMCwgMSksCiAgICAgeWxpbSA9IGMoMCwgMSkpCmBgYApGaWd1cmUgMTA6IFNob3dpbmcgZWFjaCBnZW5lIG9uIGEgZ3JhcGggd2hlcmUgdGhlIGNvb3JkaW5hdGVzIGlzIHRoZSBjb3JyZWN0ZWQgcC12YWx1ZXMgdXNpbmcgdGhlIHNpbXBsZSBtb2RlbCBpbiBnbG1RTEZpdCBhbmQgaW4gbGltbWEuIEhpZ2hsaWdodGluZyBzaWduaWZpY2FudCBnZW5lcyBpbiBib3RoIG1vZGVsIGFzIHJlZCwgaW4gc2ltcGxlIG1vZGVsIG9ubHkgYXMgb3JhbmdlLCBpbiBvcmdhbm9pZCBtb2RlbCBvbmx5IGFzIHJlZCwgYW5kIG5vbi1zaWduaWZpY2FudCBnZW5lcyBhcyBibGFjawpbQGw2XQoKRm9yIHNvbWUgcmVhc29uLCB0aGlzIGlzIGFsbCBvdmVyIHRoZSBwbGFjZQoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmNhcD0nRmlndXJlIDExOiBTaG93aW5nIGVhY2ggZ2VuZSBvbiBhIGdyYXBoIHdoZXJlIHRoZSBjb29yZGluYXRlcyBpcyB0aGUgY29ycmVjdGVkIHAtdmFsdWVzIHVzaW5nIHRoZSBzaW1wbGUgbW9kZWwgaW4gZ2xtUUxGaXQgYW5kIGluIGxpbW1hLCB6b29tZWQgaW4gb24gMC0wLjAyLiBIaWdobGlnaHRpbmcgb25seSB0aGUgZ2VuZSBvZiBpbnRlcmVzdCwgQ1JYJ30KZW5zZW1ibF9vZl9pbnRlcmVzdCA8LSBub3JtYWxpemVkX2NvdW50X2RhdGEkZW5zZW1ibF9nZW5lX2lkWwogIHdoaWNoKG5vcm1hbGl6ZWRfY291bnRfZGF0YSRoZ25jX3N5bWJvbCA9PSAiQ1JYIildCgp0d29fbW9kZWxzX3B2YWx1ZXMkY29sb3VyIDwtICJncmV5IgoKdHdvX21vZGVsc19wdmFsdWVzJGNvbG91clt0d29fbW9kZWxzX3B2YWx1ZXMkZW5zZW1ibF9pZAogICAgICAgICAgICAgICAgICAgICAgICAgID09ZW5zZW1ibF9vZl9pbnRlcmVzdF0gPC0gInJlZCIKCnBsb3QodHdvX21vZGVsc19wdmFsdWVzJHFsZl9zaW1wbGVfcHZhbHVlLAogICAgIHR3b19tb2RlbHNfcHZhbHVlcyRsaW1tYV9zaW1wbGVfcHZhbHVlLAogICAgIGNvbCA9IHR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXIsCiAgICAgeGxhYiA9ICJRTEYgc2ltcGxlIG1vZGVsIGFkanVzdGVkIHAtdmFsdWVzIiwKICAgICB5bGFiID0iTGltbWEgc2ltcGxlIG1vZGVsIGFkanVzdGVkIHAtdmFsdWVzIiwKICAgICBtYWluPSJRTEYgdnMgTGltbWEiLAogICAgIHhsaW0gPSBjKDAsIDAuMDIpLAogICAgIHlsaW0gPSBjKDAsIDAuMDIpKQoKcG9pbnRzKHR3b19tb2RlbHNfcHZhbHVlc1sKICB0d29fbW9kZWxzX3B2YWx1ZXMkZW5zZW1ibF9pZD09ZW5zZW1ibF9vZl9pbnRlcmVzdCwyOjNdLAogICAgICAgcGNoPTI0LCAgY29sPSJyZWQiLCBjZXg9MS41KQpgYGAKRmlndXJlIDExOiBTaG93aW5nIGVhY2ggZ2VuZSBvbiBhIGdyYXBoIHdoZXJlIHRoZSBjb29yZGluYXRlcyBpcyB0aGUgY29ycmVjdGVkIHAtdmFsdWVzIHVzaW5nIHRoZSBzaW1wbGUgbW9kZWwgaW4gZ2xtUUxGaXQgYW5kIGluIGxpbW1hLCB6b29tZWQgaW4gb24gMC0wLjAyLiBIaWdobGlnaHRpbmcgb25seSB0aGUgZ2VuZSBvZiBpbnRlcmVzdCwgQ1JYCltAbDZdCgpJdCBsb29rcyBsaWtlIFFMRiBpcyBiZXR0ZXIgdGhhbiBsaW1tYSwgYW5kIHRoYXQgZG9lcyBtYWtlIHNlbnNlLCBzaW5jZSBsaW1tYQp3YXMgbWFkZSBmb3IgbWljcm9hcnJheSwgYnV0IEkgYW0gdXNpbmcgYnVsayBSTkEtc2VxIGRhdGEsIHdoaWNoIGVkZ2VSIG1ldGhvZHMKYXJlIGRlc2lnbmVkIGZvci4gCgoKIyMgSGVhdG1hcCBmcm9tIGVkZ2VSCgpoZWF0bWFwLCBidXQgdXNpbmcgdG9waGl0cyBmcm9tIHFsZiBpbnN0ZWFkIG9mIGxpbW1hIApgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSdGaWd1cmUgMTI6IEhlYXRtYXAgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIG9mIHRoZSBmaXJzdCAxMDAwIGdlbmVzLCBvcmRlcmVkIGJ5IGVuc2VtYmwgaWQsIGFmdGVyIHJvdyBub3JtYWxpemF0aW9uIGZvciBzaWduaWZpY2FudCBnZW5lcyB1c2luZyB0aGUgc2ltcGxlIG1vZGVsIGluIGdsbVFMRml0LiBPbmx5IHRoZSBmaXJzdCAxMDAwIGdlbmVzIGFyZSBzaG93biBkdWUgdG8gcmVuZGVyaW5nIHJlc291cmNlIGlzc3Vlcyd9CnRvcF9oaXRzIDwtIHJvd25hbWVzKHFsZl9vdXRwdXRfaGl0cyR0YWJsZSlbCiAgcWxmX291dHB1dF9oaXRzJHRhYmxlJEZEUjwwLjA1XQoKaGVhdG1hcF9tYXRyaXhfdG9waGl0cyA8LSB0KAogIHNjYWxlKHQoaGVhdG1hcF9tYXRyaXhbd2hpY2gocm93bmFtZXMoaGVhdG1hcF9tYXRyaXgpCiVpbiUgdG9wX2hpdHMpLF0pKSkKCm5yb3coaGVhdG1hcF9tYXRyaXhfdG9waGl0cykKCmlmKG1pbihoZWF0bWFwX21hdHJpeF90b3BoaXRzKSA9PSAwKXsKaGVhdG1hcF9jb2wgPSBjb2xvclJhbXAyKGMoIDAsIG1heChoZWF0bWFwX21hdHJpeF90b3BoaXRzKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYyggIndoaXRlIiwgInJlZCIpKQp9IGVsc2UgewpoZWF0bWFwX2NvbCA9IGNvbG9yUmFtcDIoYyhtaW4oaGVhdG1hcF9tYXRyaXhfdG9waGl0cyksIDAsCiAgICAgICAgICAgICAgICAgICAgICBtYXgoaGVhdG1hcF9tYXRyaXhfdG9waGl0cykpLAogICAgICAgICAgICAgICAgICAgICAgYygiYmx1ZSIsICJ3aGl0ZSIsICJyZWQiKSkKfQoKSGVhdG1hcChhcy5tYXRyaXgoaGVhdG1hcF9tYXRyaXhfdG9waGl0c1sxOjEwMDAsXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfY29sdW1ucyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X3Jvd19kZW5kID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfY29sdW1uX2RlbmQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sPWhlYXRtYXBfY29sLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19jb2x1bW5fbmFtZXMgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19yb3dfbmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfaGVhdG1hcF9sZWdlbmQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQpgYGAKRmlndXJlIDEyOiBIZWF0bWFwIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBvZiB0aGUgZmlyc3QgMTAwMCBnZW5lcywgb3JkZXJlZCBieSBlbnNlbWJsIGlkLCBhZnRlciByb3cgbm9ybWFsaXphdGlvbiBmb3Igc2lnbmlmaWNhbnQgZ2VuZXMgdXNpbmcgdGhlIHNpbXBsZSBtb2RlbCBpbiBnbG1RTEZpdC4gT25seSB0aGUgZmlyc3QgMTAwMCBnZW5lcyBhcmUgc2hvd24gZHVlIHRvIHJlbmRlcmluZyByZXNvdXJjZSBpc3N1ZXMKW0BsNjsgQGhlYXRtYXA7IEBjb2xvcl0KCmNvbXBhcmUgdG8gdGhlIGhlYXRtYXAgZnJvbSBsZW1tYSwgdGhlIGRheSAyMDAgZG8gbm90IGNsdXN0ZXIgdG9nZXRoZXIsIApidXQgdGhlIGRheSAxNTAgYW5kIGRheSAxMjUgY2x1c3RlciBpbnRvIDIgZ3JvdXBzLiBIb3dldmVyIGFsbCBkYXkgOTAgZG8gY2x1c3Rlcgp0b2dldGhlci4gCgp0aGVuIGFsc28gY2x1c3RlciBieSBjb250cm9sIHZzIGV4cGVyaW1lbnQKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5jYXA9J0ZpZ3VyZSAxMzogSGVhdG1hcCBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gb2YgdGhlIGZpcnN0IDEwMDAgZ2VuZXMsIG9yZGVyZWQgYnkgZW5zZW1ibCBpZCwgYWZ0ZXIgcm93IG5vcm1hbGl6YXRpb24gZm9yIHNpZ25pZmljYW50IGdlbmVzIHVzaW5nIHRoZSBzaW1wbGUgbW9kZWwgaW4gZ2xtUUxGaXQsIHdpdGhvdXQgY2x1c3RlcmluZywgYW5kIGdyb3VwIGNvbnRyb2wgYW5kIGV4cGVyaW1lbnQgdG9nZXRoZXIsIHJlc3BlY3RpdmVseS4gT25seSB0aGUgZmlyc3QgMTAwMCBnZW5lcyBhcmUgc2hvd24gZHVlIHRvIHJlbmRlcmluZyByZXNvdXJjZSBpc3N1ZXMnfQp0b3BfaGl0cyA8LSByb3duYW1lcyhxbGZfb3V0cHV0X2hpdHMkdGFibGUpW3FsZl9vdXRwdXRfaGl0cyR0YWJsZSRGRFIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8MC4wNV0KaGVhdG1hcF9tYXRyaXhfdG9waGl0cyA8LSB0KAogIHNjYWxlKHQoaGVhdG1hcF9tYXRyaXhbd2hpY2gocm93bmFtZXMoaGVhdG1hcF9tYXRyaXgpCiVpbiUgdG9wX2hpdHMpLF0pKSkKCmhlYXRtYXBfbWF0cml4X3RvcGhpdHM8LSBoZWF0bWFwX21hdHJpeF90b3BoaXRzWywgCmMoZ3JlcChjb2xuYW1lcyhoZWF0bWFwX21hdHJpeF90b3BoaXRzKSxwYXR0ZXJuID0gIlxcQ1JYTENBIiksCiAgZ3JlcChjb2xuYW1lcyhoZWF0bWFwX21hdHJpeF90b3BoaXRzKSxwYXR0ZXJuID0gIlxcQ29udHJvbCIpKV0KCm5yb3coaGVhdG1hcF9tYXRyaXhfdG9waGl0cykKCmlmKG1pbihoZWF0bWFwX21hdHJpeF90b3BoaXRzKSA9PSAwKXsKaGVhdG1hcF9jb2wgPSBjb2xvclJhbXAyKGMoIDAsIG1heChoZWF0bWFwX21hdHJpeF90b3BoaXRzKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYyggIndoaXRlIiwgInJlZCIpKQp9IGVsc2UgewpoZWF0bWFwX2NvbCA9IGNvbG9yUmFtcDIoYyhtaW4oaGVhdG1hcF9tYXRyaXhfdG9waGl0cyksIDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXgoaGVhdG1hcF9tYXRyaXhfdG9waGl0cykpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoImJsdWUiLCAid2hpdGUiLCAicmVkIikpCn0KCkhlYXRtYXAoYXMubWF0cml4KGhlYXRtYXBfbWF0cml4X3RvcGhpdHNbMToxMDAwLF0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX3Jvd3MgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2NvbHVtbnMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfcm93X2RlbmQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19jb2x1bW5fZGVuZCA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sPWhlYXRtYXBfY29sLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19jb2x1bW5fbmFtZXMgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19yb3dfbmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfaGVhdG1hcF9sZWdlbmQgPSBUUlVFKQpgYGAKRmlndXJlIDEzOiBIZWF0bWFwIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBvZiB0aGUgZmlyc3QgMTAwMCBnZW5lcywgb3JkZXJlZCBieSBlbnNlbWJsIGlkLCBhZnRlciByb3cgbm9ybWFsaXphdGlvbiBmb3Igc2lnbmlmaWNhbnQgZ2VuZXMgdXNpbmcgdGhlIHNpbXBsZSBtb2RlbCBpbiBnbG1RTEZpdCwgd2l0aG91dCBjbHVzdGVyaW5nLCBhbmQgZ3JvdXAgY29udHJvbCBhbmQgZXhwZXJpbWVudCB0b2dldGhlciwgcmVzcGVjdGl2ZWx5LiBPbmx5IHRoZSBmaXJzdCAxMDAwIGdlbmVzIGFyZSBzaG93biBkdWUgdG8gcmVuZGVyaW5nIHJlc291cmNlIGlzc3VlcwpbQGw2OyBAaGVhdG1hcDsgQGNvbG9yXQoKIyBPdmVyIHJlcHJlc2VudGF0aW9uIGFuYWx5c2lzCgoKVGhpcyBpcyB0aGUgbnVtYmVyIG9mIGdlbmVzIHVwcmVndWxhdGVkIGFuZCBkb3ducmVndWxhdGVkCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9Cmxlbmd0aCh3aGljaChxbGZfb3V0cHV0X2hpdHMkdGFibGUkRkRSIDwgMC4wNSAKICAgICAgICAgICAgICYgcWxmX291dHB1dF9oaXRzJHRhYmxlJGxvZ0ZDID4gMCkpCmxlbmd0aCh3aGljaChxbGZfb3V0cHV0X2hpdHMkdGFibGUkRkRSIDwgMC4wNSAKICAgICAgICAgICAgICYgcWxmX291dHB1dF9oaXRzJHRhYmxlJGxvZ0ZDIDwgMCkpCmBgYApbQGw3XQoKSSBvbmx5IG91dHB1dHRlZCB0aGUgZmlyc3QgNTAwMCBnZW5lcywgYmVjYXVzZSBnOnByb2ZpbGVyIHdpbGwgY3Jhc2ggd2l0aCB0b28gCm1hbnkgZ2VuZXMuIAoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCnFsZl9vdXRwdXRfaGl0c193aXRoZ24gPC0gbWVyZ2UoQ1JYX0V4cGVyaW1lbnRSYXdDb3VudEZpbHRlcmVkWywxOjJdLCBxbGZfb3V0cHV0X2hpdHMsIGJ5Lng9MSwgYnkueSA9IDApCgpxbGZfb3V0cHV0X2hpdHNfd2l0aGduWywicmFuayJdIDwtIC1sb2cocWxmX291dHB1dF9oaXRzX3dpdGhnbiRGRFIsIGJhc2UgPTEwKSAqIHNpZ24ocWxmX291dHB1dF9oaXRzX3dpdGhnbiRsb2dGQykKCnFsZl9vdXRwdXRfaGl0c193aXRoZ24gPC0gcWxmX291dHB1dF9oaXRzX3dpdGhnbltvcmRlcihxbGZfb3V0cHV0X2hpdHNfd2l0aGduJHJhbmspLF0KCnVwcmVndWxhdGVkX2dlbmVzIDwtIHFsZl9vdXRwdXRfaGl0c193aXRoZ24kZ2VuZS5pZFsKICB3aGljaChxbGZfb3V0cHV0X2hpdHNfd2l0aGduJEZEUiA8IDAuMDUgCiAgICAgICAgICAgICAmIHFsZl9vdXRwdXRfaGl0c193aXRoZ24kbG9nRkMgPiAwKV0KCmRvd25yZWd1bGF0ZWRfZ2VuZXMgPC0gcWxmX291dHB1dF9oaXRzX3dpdGhnbiRnZW5lLmlkWwogIHdoaWNoKHFsZl9vdXRwdXRfaGl0c193aXRoZ24kRkRSIDwgMC4wNSAKICAgICAgICAgICAgICYgcWxmX291dHB1dF9oaXRzX3dpdGhnbiRsb2dGQyA8IDApXQoKZmlyc3Q1MDAwX2dlbmVzIDwtIHFsZl9vdXRwdXRfaGl0c193aXRoZ24kZ2VuZS5pZFtjKDE6NTAwMCldCgppZiAoIWZpbGUuZXhpc3RzKCJkYXRhIikpIHsKIGRpci5jcmVhdGUoImRhdGEiKQp9Cgp3cml0ZS50YWJsZSh4PXVwcmVndWxhdGVkX2dlbmVzLAogICAgICAgICAgICBmaWxlPWZpbGUucGF0aCgiZGF0YSIsIkNSWF91cHJlZ3VsYXRlZF9nZW5lcy50eHQiKSwgc2VwID0gIlx0IiwKICAgICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UsIGNvbC5uYW1lcyA9IEZBTFNFLCBxdW90ZSA9IEZBTFNFKQoKd3JpdGUudGFibGUoeD1kb3ducmVndWxhdGVkX2dlbmVzLAogICAgICAgICAgICBmaWxlPWZpbGUucGF0aCgiZGF0YSIsIkNSWF9kb3ducmVndWxhdGVkX2dlbmVzLnR4dCIpLHNlcCA9ICJcdCIsCiAgICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFLCBjb2wubmFtZXMgPSBGQUxTRSwgcXVvdGUgPSBGQUxTRSkKCndyaXRlLnRhYmxlKHg9Zmlyc3Q1MDAwX2dlbmVzLAogICAgICAgICAgICBmaWxlPWZpbGUucGF0aCgiZGF0YSIsIkNSWF9yYW5rZWRfZ2VuZWxpc3QudHh0IiksCiAgICAgICAgICAgIHNlcCA9ICJcdCIsCiAgICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFLAogICAgICAgICAgICBjb2wubmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgcXVvdGUgPSBGQUxTRSkKCmBgYApbQGw3XQoKCiMgUmVzdWx0cyBvZiBHOnByb2ZpbGVyCgoKCgojIERpc2N1c3Npb24gCgpXZSBoYXZlIDMgZmFjdG9ycyB0aGF0IHdlIGNhbiBpbmNsdWRlIGluIG91ciBtb2RlbCBkZXNpZ24uIFRoZSBkYXkgdGhhdCB0aGUgZGF5CndhcyBjb2xsZWN0ZWQsIHdoaWNoIG9yZ2Fub2lkIHdhcyBpdCBjb2xsZWN0ZWQgZnJvbSwgaXMgaXQgZXhwZXJpbWVudCBvciBjb250cm9sCnRoYXQgd2UgYXJlIGNvbGxlY3RpbmcgZnJvbT8KCldlIGRvIGhhdmUgdG8gaW5jbHVkZSB0aGUgbGFzdCBvbmUgKGV4cGVyaW1lbnQgdnMgY29udHJvbCksIG9yIGVsc2Ugd2UgYXJlIG5vdApnZXR0aW5nIHRoZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBmb3IgdGhlIHF1ZXN0aW9uIHRoYXQgdGhlIGF1dGhvcnMgaXMgYXNraW5nLgpbQENSWF0uIEFsdGhvdWdoIGF0IGZpcnN0LCB0aGUgY29udHJvbCBhbmQgZXhwZXJpbWVudHMgZG8gbm90IGRpZmZlciB0aGF0IG11Y2gsCmJ1dCBieSBkYXkgMjAwIHRoZXkgdGhlIGNvbnRyb2xzIGNsdXN0ZXIgdG9nZXRoZXIgbW9yZSB0aGFuIHRoZSBvcmdhbm9pZHMsIApzbyBpcyB0aGUgZXhwZXJpbWVudHMuIEkgYmVsaWV2ZSB3ZSBjYW4gY29uY2x1ZGUgdGhhdCwgYnkgZGF5IDIwMCwgdGhlIGRpZmZlcmVuY2UKZnJvbSBDUlgsIGlzIG1vcmUgdGhhbiB0aGUgb3JnYW5vaWQgdmFyaWFiaWxpdHkuIAoKTG9va2luZyBhdCB0aGUgTURTIHBsb3QgZnJvbSBBMSwgd2UgY2FuIHRlbGwgdGhhdCB0aGUgZGF5IGl0IHdhcyBjb2xsZWN0ZWQgZnJvbQpjaGFuZ2VzIHRoZSBkYXRhIGJ5IGEgbG90LCBzaW5jZSB0aGUgUk5BIGlzIHRha2VuIGZyb20gcmV0aW5hbCBvcmdhbm9pZHMgaW4gCmRldmVsb3BtZW50LCBhbmQgZ2VuZSBleHByZXNzaW9uIGZvciBjZWxsIGluIGRldmVsb3BtZW50IGNoYW5nZXMgZnJvbSBzdGFnZQp0byBzdGFnZS4gVGhlcmVmb3JlIHdlIHNob3VsZCBpbmNsdWRlIHRoaXMuCgpTdGlsbCBsb29rIGF0IHRoZSBNRFMgcGxvdCBmcm9tIEExLCB3aGljaCBvcmdhbm9pZCBpdCB3YXMgY29sbGVjdGVkIGZyb20gZG9lcwpub3QgbWFrZSBtdWNoIG9mIGEgZGlmZmVyZW5jZSwgdGhpcyBpcyBwcm9iYWJseSBiZWNhdXNlIHRoYXQgYWxsIGNvbnRyb2wKb3JnYW5vaWRzIGFyZSBmcm9tIHRoZSBzYW1lIHBlcnNvbiwgYW5kIGFsbCBleHBlcmltZW50IG9yZ2Fub2lkcyBhcmUgZnJvbSB0aGUKc2FtZSBwZXJzb24uIAoKCiMgUXVlc3Rpb25zCgoKCgojIFJlZmVyZW5jZXM=